最新要闻
- 汉语为桥 粽情西岸 当前热点
- 天天观点:究竟是驼背还是翘臀?全新奔驰GLC轿跑SUV插电混动版来了
- 马斯克和扎克伯格约架 马斯克母亲:确认取消了|世界视点
- 迪凡车业为旗下跃歌品牌保驾护航
- 理想L系列经常充到100%伤车吗? 李想:不会 放心用 电池寿命大于车|焦点快看
- 世界播报:浙江一男子躲雨后将屋主晒的菜踢到雨中 三观尽毁:网友称坏到骨子里
- 景区网红大雁被游客踢中要害离世 吓着孩子:网友愤怒 应重罚 世界动态
- 全球短讯!Win11最近一个月卡爆了!CPU占用高元凶终于找到:微软认错
- 内江第十一届大千龙舟文化节市中区端午民俗活动举行|独家
- 【世界独家】詹姆斯·卡梅隆谈泰坦号观光艇内爆导致5名乘员死亡:和泰坦尼克号沉没惊人相似 灾难再一次重演
- 微软誓要收购动视暴雪 索尼急了!PS6主机将封杀之|环球今日讯
- 前沿资讯!南孚旗下 益圆5号/7号碳性电池冲量:0.48元/节
- 车主自曝差点被闷死在特斯拉里:车辆完全死机 车门锁死_每日速讯
- 辽宁:2023年高考成绩24日16时发布|当前视点
- 当前滚动:理查森的大型CityLine办公楼开发项目上市
- 【天天新要闻】50斤重鳄雀鳝藏匿水库一年偷鱼吃 为何不负责放生屡禁不止?律师、专家释疑
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
rust 集合、错误处理、泛型、Trait、生命周期、包 全球新动态
集合组织特性相同的数据;泛型可以定义任何抽象数据类型;生命周期限制所有权的作用域范围;错误处理使程序更健壮。
(相关资料图)
集合
一组特性相同的数据集合,除了基本数据类型的元组、数组。rust 标准库提供了一些非常有用的数据结构。
Vector
存储列表
通过类型Vec
定义。只能存储相同类型的值,在内存中彼此相邻排列存储。
let v:Vec = Vec::new();
通过Vec::new()
创建一个类型实例。因为没有初始化任何类型数据,就必须指定数据类型。定义集合实例就只允许存储指定的类型数据。
另一种方便创建集合实例的方式通过 rust 提供的vec!
宏
let v = vec![3,5,6];
定义了实例v
,可以初始化数据,rust 会推导出数据的类型。示例中默认推导出类型是 i32
可以通过内部方法,操作实例来添加、修改里面的数据
v.push(val)
添加值。v.get(index)
获取值。会得到一个可用于match
匹配的Option<&T>
也可以使用索引取值
&v[1]
。使用索引取值,如果超出最大索引,会报错;使用get()
方法会返回None
v.insert(index,val)
向指定 index 位置插入数据。v.remove(index)
移除指定 index 位置的数据,并返回该数据。v.pop()
移除最后一个元素,并返回。v.clear()
清空实例。移除所有元素。v.len()
返回当前数据个数。
要可编辑实例,声明必须使用mut
可变。
let mut v:Vec = vec![];// 更新值v.push(23);v.push(4);v.push(15);v.push(56);// 取值v.get(2); // 4v[2]; // 4
在操作vec
时,注意引用所有权的转义。最好的方式就是只是值借用&v
。
通过for
循环来遍历 vector 中的值。
for i in &v{ println!("{i}");}
在遍历时实例v
不能插入、删除项。如果需要想遍历修改每一项值,可以传递可变引用
for i in &mut v { *i += 5; println!("{i}");}
因为是对值做操作。通过*
解引用取到指针指向的值。再次从实例v
取值时,都是最新计算过的值。
通过枚举存储多种类型
因为 vector 只能存储相同类型的值。实际开发中如果需要存储不同类型的值,可以使用枚举定义。
这样对于 vector 而言,它都是同一种枚举类型。
enum Color{ Red(String), Green(u32,u32,u32), Green(u32,u32,u32,u8)}fn main(){ let colors = vec![Color::Red(String::from("red")), Color::Green(0, 255, 0)]; for i in &colors { println!("{:?}", i); }}
字符串
之前已经通过String::from()
来创建一个字符串变量值。字符串是字节的集合。
在 rust 中只有一种字符串类型:字符串 slice str
;通常是以借用的方式&str
。
作为一个集合,也可以通过 new 操作符创建一个实例。
let mut str = String::new();
但是通过 new 创建是的实例不能初始化数据值。所以之前一直使用String::from()
也可以用一个字符串字面值创建 String
let s = "hboot";let str = s.to_string();
字符串是utf-8
编码的。可以包含任何可以正确编码的数据。
操作字符串,作为一个集合,也有许多更新的方法:
push_str
尾部附加字符串。不会获得变量的所有权,内部采用字符串 slice。push
尾部附加字符。
let mut s = String::from("hboot");s.push_str(" hello");s.push("A");
也可以通过+
运算符拼接字符串,运算位加值将会转义所有权,而被加值则必须引用
let s1 = String::from("hboot");let s2 = String::from("hello");let s = s1+&s2; // s1的所有权没有了,s2的所有权仍然存在
也就是只能是&str
和String
相加。不能两个String
相加,它们类型不同,确定相加是因为 rust 内部把 String 强制转换为&str
当拼接值过多时,我们可以通过format!
宏来处理。它不会获取任何字符串的所有权
let s1 = String::from("hboot");let s2 = String::from(" hello");let s3 = String::from(" world");let s = format!("{s1}{s2}{s3}");
rust 的字符串不支持索引。
- 对于字符串值在内存中是以字符编码 code 存储的,而不是字符。通过下标获取到并不是想看到的值。
- 访问效率不高。通常索引预期复杂度(O(1)),但是在 rust 中,需要需要从头开始到索引位置遍历字符是否有效。
所以遍历字符串最好的方式明确需要的是字符还是字节。字符通过chars
方法将其分开并返回多个char
类型的值;字节则使用bytes
方法返回字符的编码值。
let s1 = String::from("hboot");// 遍历获取字符for c in s1.chars() { println!("{c}");}// 遍历获取字节for c in s1.bytes() { println!("{c}");}
对于字节,有的语言编码后可能不止一个字节组成,这个需要注意。
HashMap
存储键值对
创建HashMap
实例,因为 HashMap 没有被 prelude。所以需要手动引入。
use std::collections::HashMap;fn main(){ let mut map = HashMap::new();}
当未被使用时,键值对的数据类型是unknown
。在第一次插入数据后,则决定了后面的数据类型
let mut map = HashMap::new();map.insert(1, 10);map.insert(2, 30);
此时默认类型为HashMap
。当时用 String 作为键值是,变量的所有权将被转移给 map。字符串变量不可用
let mut map = HashMap::new();let s = String::from("red");map.insert(s, "red");
通过map.get()
获取 HashMap 中的值,返回Option<&V>
,如果没有键时,则返回None
.
可以通过copied()
方法来获取Option
;如果没有键时,可以通过uwrap_or()
在没有键值时,设置一个替代值。
map.get(&String::from("yellow")).copied().unwrap_or("yellow");
注意get
方法接受是一个&str
类型。
当我们重复对同一个键赋值时,后面的会覆盖之前的。如果需要判断是否存在键,不存在插入数据;存在则不做任何操作
map.entry(String::from("green")).or_insert("green");
entry
的or_insert()
方法在键存在时会返回这个值的可变引用。不存在则将参数作为新值插入并返回值的可变引用。
一个示例,通过 HashMap
统计字符串中出现的字符数。
let s1 = String::from("hboot");let mut map = HashMap::new();for c in s1.chars() { let num = map.entry(c).or_insert(0); *num += 1;}dbg!("{:?}", map);
HashMap
默认使用了叫做 SipHash 的哈希函数,可以抵御哈希表的拒绝服务攻击。
泛型、trait 和生命周期
泛型是具体类型和其他属性的抽象替代。定义时不必知道这里实际代表什么,比如之前的实例中的Option
都已经接触了。
泛型
通过定义泛型,可以抽离一些重复的代码逻辑。使得我们的代码更具维护性、适应性更强。
创建一个泛型函数。类型参数声明必须在函数名称和参数列表中间尖括号<>
里面。
fn largest(list: &[T]) -> &T { let mut large = &list[0]; for val in list { if val > large { large = val; } } large}fn main(){ let v1 = vec![12, 34, 5, 56, 7]; let v2 = vec![34.23, 12.12, 56.1223, 23.12]; dbg!(largest(&v1)); dbg!(largest(&v2));}
实例中为了找出给定 vector 结构数据中的最大值。但是调用的两次结构实例是不同的数据类型i32、f64
,使用泛型则可以只写一个公用的函数。
泛型函数中通过遍历结构中的数据进行对比排序。但是
泛型是任何类型,存在有的数据类型不能进行排序,rust 在编译阶段会报错。所以增加了泛型限制,std::cmp::PartialOrd
标识传入的类型都可以进行排序。
在结构体使用泛型,作为数据类型。
struct Size{ width:T, height:T}
也可以传入多个泛型,对应不同的字段数据类型Size
在枚举中使用泛型。之前已经使用的枚举Option
enum Status { YES(T), NO(U),}
也可以在结构体、枚举的方法定义中使用泛型。此时需要在impl
后声明泛型T
impl Size { fn width(&self) -> &T { &self.width }}
如果在方法中,指定了具体的数据类型,那么创建的实例,不是该数据类型时,则不能调用该方法。
impl Size { fn height(&self) -> &u8 { &self.height }}fn main(){ let size1: Size = Size { width: 34, height: 45, }; let size2: Size = Size { width: 34.12, height: 45.34, }; size1.height(); // size1 实例上有height方法。size2则没有}
泛型不会使程序比具体类型运行的慢。rust 通过在编译时进行泛型代码的单态化,也就是重复将泛型声明为具体的定义。
trait
定义共同行为
什么是 trait,在之前的描述已多次出现。它定义了某个特定类型拥有可能与其他类型相同的功能。
- 可以通过
trait
以一种抽象的方式定义共享的行为。 - 可以使用
trait bounds
指定泛型是任何拥有特定行为的类型。
类比接口行为。抽象定义属性、方法,然后其他的实例创建实现接口中的方法。
通过trait
定义一个抽象方法。
trait Log { fn log(&self)->String;}
声明一个Log
tait,包含了一个方法 log。它用来记录实例创建产生行为后日志记录。
每个声明的集合数据都必须实现这个方法。
struct Size { width: T, height: T,}// std::fmt::Debug 是为了打印输出impl Log for Size { fn log(&self) -> String { let str = format!("{:?}-{:?}", &self.width, &self.height); println!("变更值:{str}"); str }}fn main(){ let mut size2: Size = Size { width: 34.12, height: 45.34, }; size2.width = 45.111; size2.log();}
也可以提供一个默认实现,这样可以选择重载这个方法或者保留默认实现。
trait Log { fn entry_log(&self) -> String { String::from("entry log...") }}
然后在其他类型实现 trait 时,可以保留默认的行为。
// 在上方实现的结构体size2,可以直接调用println!("{}", size2.entry_log());
也可以在默认实现中,调用其他方法。
trait Log { fn log(&self) -> String; fn entry_log(&self) -> String { let entry = String::from("entry log..."); println!("{}", entry); // 调用log方法 let content = self.log(); format!("{}", content) }}fn main(){ // size2 实现不变,仅需要调用entry_log方法即可 // entry.log(); size2.entry_log();}
实现了trait
这些定义后,如何将其作为参数传递呢。使用impl trait
语法
fn notify(item: &impl Log) { println!("Log! {}", item.entry_log());}fn main(){ // 通过传递实例 size2直接调用该方法 notify(&size2);}
也可以通过泛型来定义参数,专业术语称为trait bound
fn notify(item: &T) { println!("Log! {}", item.entry_log());}
这种方式在对于多个参数的书写友好。可以通过泛型限制参数的类型。
fn notify(item: &T,item1:&T) { println!("Log! {}", item.entry_log());}
也可以通过+
指定多个 trait。
fn notify(item: &(impl Log + Display)) {}// 或者使用泛型fn notify(item: &T) {}
调用传参时的实例则必须实现Log和Display
,但是当有很多个 trait 时,书写起来就会很多。
可以通过where
关键字简化书写,看起来更加的清晰。
fn notify(item: &T, item2: &U)where T: Log + Display, U: Clone + Display,{}
也可以通过函数返回某个实现了trait
的类型实例
fn return_log() -> impl Log { Size { width: 23, height: 45, }}fn main(){ let size3 = return_log(); size3.entry_log();}
在闭包和迭代器场景中十分有用。但是这种适用于返回单一类型的情况。
通过trait bound
可以有条件的控制实例可调用的类型方法。只有类型实现了某些方法,实例才会有指定的方法。
生命周期
也就是对于引用、借用的有效作用域的限制。在引用或借用之前,保证被引用或借用的变量在当前作用域一直有效。
这个特性避免了悬垂引用,防止了程序引用未定义数据的问题;如下例子:
fn main(){ let a; { let b = "admin"; a = &b; } println!("{}",a)}
运行cargo run
这段代码,将会报错,变量a
得到了局部作用域变量b
的引用,在最后的作用域中使用了a
。但是变量b
在局部作用域结束时就已经释放了,导致引用它的a
在使用时就会报错。
在 rust 中,通过借用检查器
来检测作用域之间的借用是否都是有效的。并在编译阶段给出错误提示,上面的代码不需要运行,也可以看到编译器给出的错误提示。
为了解决上面这问题,我们可以将 b
的所有权交出去,因为b
作用域结束,并没有什么作用了。
{ let b = "admin"; a = b;}
还有在第一篇文章所有权
的问题
fn print_info() -> String { let str = String::from("hboot"); // 这是错误的,函数执行完毕,必须交出所有权 // &str // 直接返回创建的字符串 str}
生命周期注解
还有一些问题,在函数调用的时候,需要传参处理完后返回某个参数的值。如下示例:
fn main(){ let a = String::from("abcd"); let b = String::from("efg"); println!("{}", longest(&a, &b));}fn longest(a: &str, b: &str) -> &str { if a.len() > b.len() { a } else { b }}
编译器直接就会提示错误信息,我们执行cargo run
看详细的错误信息。错误也很明确expected named lifetime parameter
,并且给出了解决示例。
fn longest<"a>(a: &"a str, b: &"a str) -> &"a str { if a.len() > b.len() { a } else { b }}
"a
就是生命周期的注解语法。
- 它可以给描述多个引用生命周期的关系,而不影响参数
a、b
的生命周期。 - 在函数指定了泛型生命周期,函数就可以接受任何生命周期的引用。
- 生命周期参数以
"
撇号开头,小写的名称。 - 它位于引用
&
之后,它表明了被借用的变量存在的时间和借用变量的生命周期存在的一样久 - 它保证了在引用值
a、b
中作用域最短的那个生命周期结束之前有效
需要注意的的就是最后一个,它的存在时间长久在于作用域最短的那一个
fn main(){ let a = String::from("abcd"); let result; { let b = String::from("efg"); result = longest(&a, &b); } println!("{}", result);}
函数的调用在b
的局部作用域中,调用结束后的结果值result
使用超出了b
的作用域,编译器报错。
可以把result
的使用范围局限在b
的作用域内。
{ let b = String::from("efg"); result = longest(&a, &b); println!("{}", result);}
结构体中的生命周期注解
同设置泛型一样,在结构体名称后面使用简括号<>
声明泛型生命周期。
struct User<"b> { name: &"b str,}fn main(){ let name = String::from("hboot"); let user = User { name: &name };}
结构体的实例user
的生命周期不能比字段name
的引用存在的更久。
生命周期注解是为了 rust 检查器推断出引用的生命周期。有时候就会书写大量的这种模板式的注解,这种场景有时候 rust 会纳入到编译器中,这样就不在显示声明,而这些模式统称为生命周期省略规则
。我们在书写时,只要总训这些规则就可以不用声明式书写生命周期了。
编译器推断生命周期的规则:
- 编译器为每一个输入的参数都分配一个生命周期参数
- 如果只有一个输入参数,那么它的生命周期参数赋予给所有的输出生命周期
- 如果有多个输入参数,其中之一个参数是
&self
或&mut self
,所有输出生命周期被赋予 self 的生命周期。
static
静态生命周期
通过static
声明一个静态生命周期,它存活于整个程序运行期间。
let str:&"static str = "hello rust";
str
文本直接存储在程序的二进制文件中。在使用时考虑是否真的需要。
包、create
通过拆解模块来创建多个文件组织代码。更好的重用代码,定义哪些内容可以公开,哪些是私有的。
这里有一些概念:包
- Cargo 的一个功能,允许构建、测试和分享 crate。crates
- 一个模块的树形结构,它形成了库或者二进制项目。模块/use
- 允许控制作用域和路径的私有性。路径
- 命名例如结构体、函数或模块等方式
包、crate
crate
分为库和二进制。二进制可以被编译为可执行文件,有一个main
函数来执行程序需要做的事情;库用来作为工具,提供诸如函数的功能。
包是一些列功能的一个或多个crate
。包含Cargo.toml
文件,阐述如何去构建这些 crate。
往往src/lib.rs
就表示这是一个库;而src/main.rs
表示这是一个包。这也是编译时的入口点。
通过mod
声明一个模块,通过内联方式声明mod user{}
;或者创建文件src/user.rs
或者src/user/mod.rs
// 内联声明mod user {}
声明好模块后,要想在其他地方使用该模块,则需要加pub
修饰,标识这是一个公用模块。
假设现在我们有以文件创建的模块src/user.rs
,其中有两个声明的公用结构和枚举类。
pub struct model { name: String, age: i32,}pub enum status { online, offline,}
通过mod
关键字定义,来说明编译器在src/user.rs
查找代码。
// 在main.rs中mod user;fn main(){ // 可以直接通过模块名称来使用定义在模块中的类型 let status = user::status::offline;}
也可以通过use
关键字来导入需要使用的公用类型。
// 在main.rs中use crate::user::status;mod user;fn main(){ let status = status::offline;}
在一个模块中,也可以继续声明子模块。声明的方式同上
引用模块路径
刚才使用模块引入的方式crate::user
是以 crate 跟开头的全路径。也可以相对于当前模块开始,以self
或者super
// 在main.rs中use user::status;mod user;
由于模块 user 和main.rs
是在同一路径下,所以可以通过相对路径引入。
如果模块层级嵌套,不在同一路径下,要想使用相对路径,可通过super
相当于..
,从父模块的路径引入。
// 在模块user中定义子模块mod work { use super::model; use super::status; fn is_working(user: model) -> String { match user.status { status::online => String::from("在线"), status::offline => String::from("离线"), } }}
虽然它们在同一文件中,但是work
定义为子模块,有自己的作用域。所以不能直接访问父模块中定义的类型。可通过super
引用。
self
则表示自己,调用自己模块中的定义。
mod work { use super::model; use super::status; fn is_working(user: model) { // 直接调用 init_user(); // 通过self调用 self::init_user(); } fn init_user() {}}
pub
声明的公用方法、类型,对于结构体,它的字段却是私有的。如果想要创建实例,则必须声明字段为公用。
// 在main.rs中use user::{model, status};mod user;fn main(){ // 下面这个创建时编译不过的,错误提示字段私有。 let u = model { name: String::from("admin"), age: 35, status: status::offline, };}
但是对于子模块引入使用时,这些字段默认都是有效可用的。
mod work { use super::model; use super::status; fn init_user() { let user = model { name: String::from("hboot"), age: 34, status: status::online, }; }}
对于外部引入模块的结构体时,如果有私有属性,则需要提供实例化方法。
// 调整src/user.rs ,提供实例化方法pub struct model { pub name: String, age: i32, status: status,}impl model { pub fn new(name: String) -> Self { Self { name, age: 35, status: status::online, } }}// 在src/main.rsfn main(){ let u = model::new(String::from("hboot"));}
通过use
引入,如果遇到同名类型时,引入路径可以只写到模块名称,然后通过模块名称调用方法、类型。
// 在main.rs中use user::work;mod user;fn main(){ let u2 = work::init_user(); print!("{:?}", u2)}
注意上面实例可被打印,修改模块 user 定义的结构体、枚举#[derive(Debug)]
也可通过 as
关键字提供一个别名。
// 在main.rs中use user::work::init_user as initUser;mod user;fn main(){ let u2 = initUser();}
可通过pub use
继续导出到外部作用域使用。这可以避免路径过长引入,可以将子模块的定义导入到父级模块中。再重导出。
pub use user::work;
这样对于当前作用域路径的上级,可以继续导入 work 模块使用。
当一个功能模块子模块很多时,就需要从一个模块中导出很多的类型、结构体、方法等。就会出现很多use
行,使用嵌套路径路径消除这种引入。
上面的示例已经展示了如何引入多个定义类型。
use user::model;use user::status;// 嵌套一行搞定use user::{model, status};
也可以通过 glob 运算符*
导入所有的公用项。
use crate::user::*
错误处理
在程序遇到错误时,分为可恢复和不可恢复。可恢复问题比如访问数据、文件未访问到,可通过日志方式告知用户;不可恢复问题比如越界,需要中止执行。
通过Result
处理可恢复的错误;panic!
宏处理不可恢复的错误。终止程序执行。
通过panic!
可以直接抛出一个错误。
fn main() { panic!("hello world");}
程序执行到此处会终止执行,并报出错误打印从出 hello world。可以看到错误出现的代码位置信息
通过错误提示,可以设置环境变量RUST_BACKTRACE=1
,查看调用栈信息
$> RUST_BACKTRACE=1 cargo run
发布生产环境包时,可以将panic
禁止掉,从而得到更小的二进制文件。
# Cargo.toml[profile.release]panic="abort"
处理可恢复的错误
有一些错误不影响程序允许的情况,我们需要给出错误时得处理方案。
Result
枚举类,标识程序方案按预期或者错误。
enum Result { Ok(T), Err(E),}
Result
及其成员被提前导入。
比如读取文件时,如果文件存在,则读取成功,状态为Ok
,类型 T 则为std::fs::File
文件
use std::fs::File;fn main(){ let read_fs = File::open("hello.txt"); // 通过match let read_file = match read_fs { Ok(file) => file, Err(error) => panic!("error info:{:#?}", error), };}
因为我们文件目录下没有hello.txt
文件,就会执行中断,报错。我们处理错误时,如果是文件未找到,则直接从创建一个。
use std::fs::File;use std::io::ErrorKind;fn main(){ let read_fs = File::open("hello.txt"); let read_file = match read_fs { Ok(file) => file, Err(error) => match error.kind() { ErrorKind::NotFound => match File::create("hello.txt") { Ok(f) => f, Err(err) => panic!("error in create file:${:?}", err), }, other_error => { panic!("error info:{:#?}", error) } }, };}
执行后,在项目根目录下会生成hello.txt
文件。如果是没有权限访问时,则还是打印输出错误。
我们写了很多的match
来处理不同的情况,这看起来很让人难以理解。通过unwrap
和expect
简写处理
unwrap()
方法调用,如果文件访问到,则返回Ok
;读取不到则返回Err
.
let read_file = File::open("hello.txt").unwrap();
通过expect()
方法调用可以达到同样的功能。但是它允许我们自定义错误信息。
let read_file = File::open("hello.txt").expect("无法读取 hello.txt!!!");
有一些错误在每个方法中处理重复、麻烦。可以将错误信息传递到调用方,然后统一处理。
use std::fs::File;use std::io::{self, ErrorKind, Read};fn read_file() -> Result { let file_result = File::open("hello.txt"); let mut file_name = match file_result { Ok(file) => file, Err(e) => return Err(e), }; let mut name = String::new(); match file_name.read_to_string(&mut name) { Ok(_) => Ok(name), Err(e) => Err(e), }}fn main(){ let read = read_file(); println!("{:?}", read)}
hello.txt
中写一段话,则会被打印出来。删除hello.txt
文件,则打印的是错误信息。
通过运算符?
简写,处理错误信息,替代match
的返回错误信息。
fn read_file() -> Result { let mut file_name = File::open("hello.txt")?; let mut name = String::new(); file_name.read_to_string(&mut name)?; Ok(name)}
使用?
简化了很多代码,还可以通过链式调用操作,使代码更为简短。
fn read_file() -> Result { let mut name = String::new(); File::open("hello.txt")?.read_to_string(&mut name)?; Ok(name)}
rust 还提供了更为方便的写法,fs::read_to_string
,它做了这些事情:打开文件、新建一个 String、读取文件的内容。将内容放入 String,并返回它。
fn read_file() -> Result { fs::read_to_string("hello.txt")}
?
运算符只能用在返回值为Result
类型的方法中。
什么情况下使用panic!
在更多的情况,我们都希望程序不要中断执行,所以处理结果返回Result
类型是最好的选择。
还有一些,希望不执行的情况。
- 示例代码、测试运行出错时,可以中断执行,执行
panic!
- 当我们很明确某种情况下程序是不能继续运行的。并给出错误信息和其他有用的信息,
- 创建自定义类型进行有效性验证
panic!
代表了无法处理的错误。停止执行以防止代码继续执行出现的不可预估的错误。
关键词:
-
MosaicML 推出 30B 模型 — 挑战 LLaMA、Falcon 和 GPT-全球今日报
![mosaic](https: img2023 cnblogs com other 618196 202306 618196-2
来源: rust 集合、错误处理、泛型、Trait、生命周期、包 全球新动态
MosaicML 推出 30B 模型 — 挑战 LLaMA、Falcon 和 GPT-全球今日报
天天即时看!mongodb 深度分页优化思路之cursor游标
记录liunx服务器和docker时区修改-天天热头条
汉语为桥 粽情西岸 当前热点
天天观点:究竟是驼背还是翘臀?全新奔驰GLC轿跑SUV插电混动版来了
马斯克和扎克伯格约架 马斯克母亲:确认取消了|世界视点
热文:https://editor.csdn.net/md/?articleId=131348876
迪凡车业为旗下跃歌品牌保驾护航
理想L系列经常充到100%伤车吗? 李想:不会 放心用 电池寿命大于车|焦点快看
世界播报:浙江一男子躲雨后将屋主晒的菜踢到雨中 三观尽毁:网友称坏到骨子里
景区网红大雁被游客踢中要害离世 吓着孩子:网友愤怒 应重罚 世界动态
全球短讯!Win11最近一个月卡爆了!CPU占用高元凶终于找到:微软认错
内江第十一届大千龙舟文化节市中区端午民俗活动举行|独家
【世界独家】詹姆斯·卡梅隆谈泰坦号观光艇内爆导致5名乘员死亡:和泰坦尼克号沉没惊人相似 灾难再一次重演
微软誓要收购动视暴雪 索尼急了!PS6主机将封杀之|环球今日讯
前沿资讯!南孚旗下 益圆5号/7号碳性电池冲量:0.48元/节
车主自曝差点被闷死在特斯拉里:车辆完全死机 车门锁死_每日速讯
辽宁:2023年高考成绩24日16时发布|当前视点
iOS 微信、支付宝、银联支付组件的进一步设计-天天播资讯
当前滚动:理查森的大型CityLine办公楼开发项目上市
【天天新要闻】50斤重鳄雀鳝藏匿水库一年偷鱼吃 为何不负责放生屡禁不止?律师、专家释疑
女子称41℃高温把盆里的衣服晒冒烟了!多地今日最高温40度:热热热
马斯克和扎克伯格约架 马斯克母亲发话:打打嘴仗算了|全球快资讯
世界速读:1. Spring相关概念
巡演拯救经济?外媒称霉霉巡演可能为美国带来46亿美元收入
土耳其央行2年来首次加息 焦点消息
真八角笼中决斗!马斯克发战书 扎克伯格接受了:科技大佬约架 都认真对待
端午假期遇上高考查分 各地高考成绩今天起陆续公布:各省成绩查询时间一览
天天快消息!张绍刚你好狠的心 剧中亲儿子都舍得杀!《长风渡》热度破万 你追了吗
地表最强三缸机!丰田全新GR卡罗拉赛道版限量发布:三缸榨出300匹马力
前端Vue自定义导航栏菜单 定制左侧导航菜单按钮 中部logo图标 右侧导航菜单按钮
夏日健康帖丨出现这几个症状别大意!你可能已经中暑|每日热文
苹果尴尬!iOS系统比安卓难上手实锤了:易用性相差58% 环球讯息
正式牵手!苏州和上海地铁连起来了:无感换乘 比坐高铁方便多了-每日观点
端午“龙舟”天花板!广西桂林遇龙河现“巨龙巡游”:无比壮观
每日报道:国产操作系统赶超Win 10:插上U盘就能用
泰坦号壳体破裂沉没 乘员无一生还 世界热点评
创两月以来最大跌幅!特斯拉市值一夜蒸发3400亿元
被特斯拉坑了 半幅方向盘车型暂停上牌?多方回应 天天观点
读发布!设计与部署稳定的分布式系统(第2版)笔记09_一窝蜂和容量 全球最资讯
满汉全席所有菜谱(求满汉全席食谱 谁有)|天天最资讯
环球快看:凤凰古城端午热:众人跳水抓鸭 场面火爆!
megacli_sw服务器Raid卡的设置过程 环球快播
高温下北京白天现星星:肉眼可见金星、月亮同框|焦点短讯
视讯!Epic喜加二:《猎人:野性的呼唤》《遗忘国度之闲置冠军》免费
《最终幻想16》等级上限解答:新游戏+解锁100级-世界观速讯
爱奇艺王晓晖:影视不是一门好生意,却可以是伟大的生意
风靡至今什么意思(风靡一时是什么意思)
清远职业技术学校是大专还是中专_清远职业技术学校
焦点热议:使用近10年,说说向大家推荐Linux系统的理由
【世界速看料】吃下肚的粽子是用什么叶子包的?知道真相的人太少了!
中央气象台发布今年首个高温橙色预警:17站点最高气温破历史极值!
随着快船错过布罗格登,这件事情彻底暴露了3个可笑的事实
人人可用的开源数据可视化分析工具 焦点快报
文心一言 VS 讯飞星火 VS chatgpt (44)-- 算法导论6.1 1题_每日热议
天天讯息:周润发《别叫我“赌神”》豆瓣短评出炉:烂片没的说!
奇葩操作 男子让狗舔舐伤口消毒 医生:狗唾液细菌很多
【全球热闻】中央气象台发布今年首个高温橙色预警 17站点最高气温破历史极值
天天新消息丨关于高考一分一档数据分布的研究
每日简讯:《英雄联盟》夏季赛EDG 0:2不敌RNG:Uzi面对老东家无力回天
内蒙古乌兰浩特:民族团结庆端午
介绍CocosCreator系统事件是怎么产生及触发的
ChatGPT 会取代程序员吗?揭穿神话 世界观天下
7999元起 国人买iPhone 14 Pro需9.5%年收入引热议:你多久才能买?
外星人在美一户人家后院活动?别信:3D建模软件做的_当前资讯
寻找新的 AI 应用程序和 ChatGPT 工具的前 5 个网站
部署zabbix5_每日资讯
北京通州一业主私家车长期占用消防车通道且拒不整改 遭行政拘留7日
科学家付巧妹获得联合国首位阿勒福赞奖 从进化角度为人类健康问题带来新见解
郑州将于6月份发放5000万元汽车消费券 不限购买者户籍
著名球星贝克汉姆使用中文为中国粉丝献上端午祝福 网友热情回应
微软宣布Xbox Series X和XGP订阅服务将涨价 称其反应市场的竞争情况
美国亿万富翁家庭以91万元年薪招聘住家狗保姆 将狗的幸福放在首位
顺丰旗下大型无人机FH-98顺利降落 最大起飞重量5.25吨
余承东曾预告!中国自动驾驶迎来大进展:官方首次表态L3商用
天天微资讯!《英雄联盟》新英雄纳亚菲利玩法公布 这是真的狗!
为减少内卷 四家头部猪企发起“互不挖人公约”
贵州贵阳周边现奇幻红色湖泊 吸引众多游客前往打卡
第28届白玉兰奖评委见面会举行 《庆余年》导演称应杜绝文盲演员
吉林一市民驾车偶遇“拦路虎” 强调路边遇到老虎不要开车窗或下车
《超级马里奥兄弟 惊奇》公布 预计于本年10月20日发售
我国最长的深水油气管道完工 区域最大作业水深近1000米
辽宁铁岭一位母亲带着高考结束的女儿去整容 当事人提醒勿盲目去跟风
北京宣布对电动自行车进行管理 因含有尖锐的金属杆导致安全系数高
视讯!中国乡村“现象级”体育赛事频现
曾排名国内服装品牌第一:拉夏贝尔正式破产清算 世界快讯
世界今日讯!Java基础复习笔记详细版
xxx.opt-1.pyc、xxx.opt-2.pyc和xxx.pyc的区别-热点在线
每日热议!redis简单介绍和使用
尤文官方:35岁迪马利亚自由身离队,据悉将加盟本菲卡
高考生注意!明天起多地高考出分_世界新要闻
全球微动态丨盘点粽子界“显眼包”:各种奇葩口味粽子层出不穷
订单爆了?日产就奇骏e-POWER发布情况说明:满30天未提车获补偿|每日快报
Go-指针篇 世界滚动
首屈一指的首屈是什么意思(首屈一指的指是哪个指)_最资讯
长白山人参鲜参大促!10支独立包装券后仅39.9:煲汤超营养|报道
【天天新视野】99%人没用过的电视隐藏功能 宅家也能爽翻天
今日热搜:西湖边上卖花郎和卖花姑娘走红 本人:希望学习分享中国传统文化
京东四面面经整理|环球百事通