Rust简明教程第六章-错误处置惩罚&生命周期

[复制链接]
发表于 2026-2-27 15:33:18 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×

theme: github
highlight: an-old-hope


观看B站软件工艺师杨旭的rust教程学习纪录,有删减有增补
错误处置惩罚



  • 可规复错误:如文件未找到可再次实验

    • Result<T,E>

  • 不可规复错误:如访问索引越界

    • panic!宏
    • 打印错误信息
    • 睁开(unwind)清算调用栈(stack),或设置制止(abort)

  • 想让二进制文件更小,可以把睁开改为制止

    • 在Cargo.toml中的[profile.release]设置panic='abort'

睁开清算调用栈会沿着栈往回走并清算每个碰到的数据
中断不举行清算直接制止步伐,内存由os清算
不可规复的错误

panic
  1. fn main(){
  2.     let v = vec![1,2,3];
  3.     println!("{}",v[5]);//越界,
  4. }
复制代码
设置RUST_BACKTRACE=1表现回溯信息,错误代码以上是我们调用的代码,错误代码以下是调用main.rs的代码
  1. #win
  2. set RUST_BACKTRACE=1 && cargo run
  3. #linux&macos
  4. RUST_BACKTRACE=1 && cargo run
复制代码
可规复的错误

Result在prelude中,不须要显式导入
  1. enum Result<T, E> {
  2.     Ok(T),
  3.     Err(E),
  4. }
复制代码


  • T 代表乐成时返回的 Ok 成员中的数据的范例
  • E 代表失败时返回的 Err 成员中的错误的范例
match处置惩罚错误
  1. use std::fs::File;
  2. fn main() {
  3.     let f = File::open("hello.txt");
  4.     let f = match f {
  5.         Ok(file) => file,
  6.         Err(error) => {
  7.             panic!("打开文件发生错误:{:?}", error)
  8.         }
  9.     };
  10. }
复制代码
match匹配差异的错误
打开文件时,文件存在则返回文件内容,文件不存在则创建文件并返回文件,创建失败制止步伐
  1. use std::{fs::File, io::ErrorKind};
  2. fn main() {
  3.     let f = File::open("hello.txt");
  4.     let f = match f {
  5.         Ok(file) => file, //返回文件内容
  6.         Err(error) => match error.kind() {
  7.             ErrorKind::NotFound => match File::create("hello.txt") {
  8.                 Ok(fc) => fc,
  9.                 Err(e) => panic!("创建文件出错!{:?}", e),
  10.             },
  11.             other_error => panic!("打开文件出错!{:?}", other_error),
  12.         },
  13.     };
  14. }
复制代码
闭包改良
  1. use std::{fs::File, io::ErrorKind};
  2. fn main() {
  3.     let _f = File::open("hello.txt").unwrap_or_else(|error| {//调用闭包
  4.         if error.kind() == ErrorKind::NotFound {
  5.             File::create("hello.txt").unwrap_or_else(|error| {
  6.                 panic!("创建错误!{:?}", error);
  7.             })
  8.         } else {
  9.             panic!("打开错误!{:?}", error);
  10.         }
  11.     });
  12. }
复制代码
unwrap

Ok则返回Ok里的内容,Err则调用panic!宏
  1. use std::fs::File;
  2. fn main() {
  3.     let f = File::open("hello.txt");
  4.     let f = match f {
  5.         Ok(file) => file,
  6.         Err(error) => {
  7.             panic!("打开文件出错!{:?}", error);
  8.         }
  9.     };
  10.     //等价于
  11.     let f = File::open("hello.txt").unwrap();
  12. }
复制代码
expect

Ok则返回Ok里的内容,Err则调用panic!宏,可以指定错误信息
  1. use std::fs::File;
  2. fn main() {
  3.     let f = File::open("hello.txt").expect("打开文件出错!");
  4. }
复制代码
流传错误

将错误返回给调用者
  1. use std::fs::File;
  2. use std::io::{self, Read};
  3. fn main() {
  4.     let result = read_username_from_file();//错误信息传播到调用者
  5.     println!("{:?}", &result);
  6. }
  7. fn read_username_from_file() -> Result<String, io::Error> {
  8.     let f = File::open("hello.txt");
  9.     let mut f = match f {
  10.         Ok(file) => file,        //成功打开文件返回文件并继续
  11.         Err(e) => return Err(e), //打开失败返回错误信息
  12.     };
  13.     let mut s = String::new();
  14.     match f.read_to_string(&mut s) {
  15.         //读取文件内容
  16.         Ok(_) => Ok(s),   //读取成功返回信息
  17.         Err(e) => Err(e), //读取失败返回错误
  18.     } //返回结果
  19. }
复制代码
?流传错误,与上面的代码功能类似,
  1. use std::fs::File;
  2. use std::io::{self, Read};
  3. fn main() {
  4.     let result = read_username_from_file(); //错误信息传播到调用者
  5.     println!("{:?}", &result);
  6. }
  7. fn read_username_from_file() -> Result<String, io::Error> {
  8.     let mut f = File::open("hello.txt")?;
  9.     let mut s = String::new();
  10.     f.read_to_string(&mut s)?;//Result为Ok,Ok()中的值就是表达式的结果,Result为Err,Err就是整个函数的返回值
  11.     Ok(s)
  12. }
复制代码
链式调用
  1. use std::fs::File;
  2. use std::io::{self, Read};
  3. fn main() {
  4.     let result = read_username_from_file(); //错误信息传播到调用者
  5.     println!("{:?}", &result);
  6. }
  7. fn read_username_from_file() -> Result<String, io::Error> {
  8.     let mut s = String::new();
  9.     File::open("hello.txt")?.read_to_string(&mut s)?;
  10.     Ok(s)
  11. }
复制代码
生命周期

克制悬垂引用(danging reference),x的生命周期比r短
  1. fn main() {
  2.     let r;
  3.     {
  4.         let x = 5;
  5.         r = &x;//x离开作用域,drop x
  6.     }
  7.     println!("r:{}", r);//借用了一个不存在的变量
  8. }
复制代码


  • Rust每个引用都有自己的生命周期
  • 生命周期是引用保持有效的作用域
  • 大多数情况:生命周期是隐式的、可被推断的
  • 当引用的生命周期大概以差异的方式相互关联时,须要手动标注生命周期
  • 现实生命周期是标注中生命周期较小的一个
  1. //告诉借用检查器函数参数(x、y)、返回值(str)的生命周期和函数的生命周期一样
  2. //实际生命周期是标注中生命周期较小的x
  3. fn find_smallest<'a>(x: &'a str, y: &'a str) -> &'a str {
  4.     if x < y {
  5.         x
  6.     } else {
  7.         y
  8.     }
  9. }
  10. fn main() {
  11.     let x = "apple";
  12.     let y = "banana";
  13.     let result = find_smallest(x, y);
  14.     println!("最短的字符串: {}", result);
  15. }
复制代码
指定生命周期参数的精确方式依赖函数实现的具体功能
  1. //返回值的生命周期只与x有关,y可以不标注
  2. fn find_smallest<'a>(x: &'a str, y: &str) -> &'a str {
  3.     x//返回x
  4. }
  5. fn main() {
  6.     let x = "apple";
  7.     let y = "banana";
  8.     let result = find_smallest(x, y);
  9.     println!("最短的字符串: {}", result);
  10. }
复制代码
从函数返回引用时,返回范例的生命周期参数要与此中一个参数的生命周期匹配
  1. fn find_smallest<'a>(x: &'a str, y: &str) -> &'a str {
  2.     let s1 = String::from("hello world");
  3.     //返回s1的切片,也就是对s1的引用,离开函数后s1被drop,该函数返回了一个指向未知数据的引用
  4.     s1.as_str()
  5. } //drop s1
  6. fn main() {
  7.     let x = "apple";
  8.     let y = "banana";
  9.     let result = find_smallest(x, y);
  10.     println!("最短的字符串: {}", result);
  11. }
复制代码


  • 生命周期在函数方法的参数中:输入生命周期
  • 生命周期在函数方法的返回值中:输出生命周期
生命周期省略规则:



  • 每个引用范例的参数都有自己的生命周期
  • 假如只有1个输入生命周期,那么该生命周期被赋给全部的输出生命周期
  • 假如有多个输入生命周期,但此中一个参数是&self或&mut self(仅方法),那么self的生命周期会被赋给全部的输出生命周期
  1. struct Foo<'a> {//可以不标注,在impl里标注
  2.     data: &'a i32, //每个引用类型的参数都有自己的生命周期,可以不标注
  3. }
  4. struct Foo2 {}
  5. impl<'a> Foo<'a> {
  6.     //如果只有1个输入生命周期,那么该生命周期被赋给所有的输出生命周期
  7.     fn new(data: &'a i32) -> Foo<'a> {//这个生命周期会被推断出来,可以不标注
  8.         Foo { data }
  9.     }
  10.     //如果只有1个输入生命周期,那么该生命周期被赋给所有的输出生命周期
  11.     fn get_data(&self) -> &'a i32 {//这个生命周期会被推断出来,可以不标注
  12.         self.data
  13.     }
  14.     //如果有多个生命周期,但其中一个参数是`&self`或`&mut self`(仅方法),那么`self`的生命周期会被赋给所有的输出生命周期
  15.     fn modify_data(&mut self, new_data: &'a i32) -> i32 {//可以推断返回值生命周期,但impl生命周期为'a,确保引用有效性,需要标注生命周期
  16.         self.data = new_data;
  17.         12 //这个返回值没有意义
  18.     }
  19.     //如果有多个生命周期,但其中一个参数是`&self`或`&mut self`(仅方法),那么`self`的生命周期会被赋给所有的输出生命周期
  20.     fn combine_data1<'b>(&'a self, other: &'a i32) -> &i32 {//可以推断返回值的生命周期,但是要确保引用有效性,需要对参数和函数标注生命周期
  21.         if *self.data > *other {
  22.             self.data
  23.         } else {
  24.             other
  25.         }
  26.     }
  27. }
  28. impl Foo2 {
  29.     //不满足推断规则需要对参数返回值标注生命周期
  30.     fn combine_data2<'q>(a: &'q i32, b: &'q i32) -> &'q i32 {
  31.         if a < b {
  32.             a
  33.         } else {
  34.             b
  35.         }
  36.     }
  37. }
  38. fn main() {
  39.     let x = 5;
  40.     let y = 10;
  41.     let mut foo = Foo::new(&x);
  42.     println!("初始化的值: {}", foo.get_data()); //初始化的值: 5
  43.     foo.modify_data(&y);
  44.     println!("修改后的值: {}", foo.get_data()); //修改后的值: 10
  45.     let combined_data1 = foo.combine_data1(&y);
  46.     println!("返回最大的: {}", combined_data1); //返回最大的: 10
  47.     let combine_data2 = Foo2::combine_data2(&x, &y);
  48.     println!("返回最小的:{}", combine_data2); //返回最小的:5
  49. }
复制代码
struct字段生命周期


  • 在struct背面标注
  • 在impl后标注
  • 这些生命周期是struct范例的一部分
impl块里的生命周期


  • 引用必须绑定于字段引用的生命周期
静态生命周期

'static:整个步伐的连续时间


  • 全部的字符串字面值都拥有'static生命周期
  1. fn main() {
  2.     let s1: &'static str = "hello world";
  3.     //等价于
  4.     let s2: &str = "hello owrld";
  5. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表