欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > Rust简明教程第六章-错误处理生命周期

Rust简明教程第六章-错误处理生命周期

2024/10/24 4:31:09 来源:https://blog.csdn.net/weixin_62799021/article/details/140216426  浏览:    关键词:Rust简明教程第六章-错误处理生命周期

theme: github
highlight: an-old-hope

观看B站软件工艺师杨旭的rust教程学习记录,有删减有补充

错误处理

  • 可恢复错误:如文件未找到可再次尝试
    • Result<T,E>
  • 不可恢复错误:如访问索引越界
    • panic!
    • 打印错误信息
    • 展开(unwind)清理调用栈(stack),或设置终止(abort)
  • 想让二进制文件更小,可以把展开改为终止
    • 在Cargo.toml中的[profile.release]设置panic='abort'

展开清理调用栈会沿着栈往回走并清理每个遇到的数据
中止不进行清理直接停止程序,内存由os清理

不可恢复的错误

panic

fn main(){let v = vec![1,2,3];println!("{}",v[5]);//越界,
}

设置RUST_BACKTRACE=1显示回溯信息,错误代码以上是我们调用的代码,错误代码以下是调用main.rs的代码

#win
set RUST_BACKTRACE=1 && cargo run
#linux&macos
RUST_BACKTRACE=1 && cargo run

可恢复的错误

Resultprelude中,不需要显式导入

enum Result<T, E> {Ok(T),Err(E),
}
  • T 代表成功时返回的 Ok 成员中的数据的类型
  • E 代表失败时返回的 Err 成员中的错误的类型

match处理错误

use std::fs::File;
fn main() {let f = File::open("hello.txt");let f = match f {Ok(file) => file,Err(error) => {panic!("打开文件发生错误:{:?}", error)}};
}

match匹配不同的错误

打开文件时,文件存在则返回文件内容,文件不存在则创建文件并返回文件,创建失败终止程序

use std::{fs::File, io::ErrorKind};
fn main() {let f = File::open("hello.txt");let f = match f {Ok(file) => file, //返回文件内容Err(error) => match error.kind() {ErrorKind::NotFound => match File::create("hello.txt") {Ok(fc) => fc,Err(e) => panic!("创建文件出错!{:?}", e),},other_error => panic!("打开文件出错!{:?}", other_error),},};
}

闭包改良

use std::{fs::File, io::ErrorKind};
fn main() {let _f = File::open("hello.txt").unwrap_or_else(|error| {//调用闭包if error.kind() == ErrorKind::NotFound {File::create("hello.txt").unwrap_or_else(|error| {panic!("创建错误!{:?}", error);})} else {panic!("打开错误!{:?}", error);}});
}

unwrap

Ok则返回Ok里的内容,Err则调用panic!宏

use std::fs::File;
fn main() {let f = File::open("hello.txt");let f = match f {Ok(file) => file,Err(error) => {panic!("打开文件出错!{:?}", error);}};//等价于let f = File::open("hello.txt").unwrap();
}

expect

Ok则返回Ok里的内容,Err则调用panic!宏,可以指定错误信息

use std::fs::File;
fn main() {let f = File::open("hello.txt").expect("打开文件出错!");
}

传播错误

将错误返回给调用者

use std::fs::File;
use std::io::{self, Read};
fn main() {let result = read_username_from_file();//错误信息传播到调用者println!("{:?}", &result);
}
fn read_username_from_file() -> Result<String, io::Error> {let f = File::open("hello.txt");let mut f = match f {Ok(file) => file,        //成功打开文件返回文件并继续Err(e) => return Err(e), //打开失败返回错误信息};let mut s = String::new();match f.read_to_string(&mut s) {//读取文件内容Ok(_) => Ok(s),   //读取成功返回信息Err(e) => Err(e), //读取失败返回错误} //返回结果
}

?传播错误,与上面的代码功能相同,

use std::fs::File;
use std::io::{self, Read};
fn main() {let result = read_username_from_file(); //错误信息传播到调用者println!("{:?}", &result);
}
fn read_username_from_file() -> Result<String, io::Error> {let mut f = File::open("hello.txt")?;let mut s = String::new();f.read_to_string(&mut s)?;//Result为Ok,Ok()中的值就是表达式的结果,Result为Err,Err就是整个函数的返回值Ok(s)
}

链式调用

use std::fs::File;
use std::io::{self, Read};
fn main() {let result = read_username_from_file(); //错误信息传播到调用者println!("{:?}", &result);
}
fn read_username_from_file() -> Result<String, io::Error> {let mut s = String::new();File::open("hello.txt")?.read_to_string(&mut s)?;Ok(s)
}

生命周期

避免悬垂引用(danging reference),x的生命周期比r短

fn main() {let r;{let x = 5;r = &x;//x离开作用域,drop x}println!("r:{}", r);//借用了一个不存在的变量
}
  • Rust每个引用都有自己的生命周期
  • 生命周期是引用保持有效的作用域
  • 大多数情况:生命周期是隐式的、可被推断的
  • 当引用的生命周期可能以不同的方式相互关联时,需要手动标注生命周期
  • 实际生命周期是标注中生命周期较小的一个
//告诉借用检查器函数参数(x、y)、返回值(str)的生命周期和函数的生命周期一样
//实际生命周期是标注中生命周期较小的x
fn find_smallest<'a>(x: &'a str, y: &'a str) -> &'a str {if x < y {x} else {y}
}fn main() {let x = "apple";let y = "banana";let result = find_smallest(x, y);println!("最短的字符串: {}", result);
}

指定生命周期参数的正确方式依赖函数实现的具体功能

//返回值的生命周期只与x有关,y可以不标注
fn find_smallest<'a>(x: &'a str, y: &str) -> &'a str {x//返回x
}
fn main() {let x = "apple";let y = "banana";let result = find_smallest(x, y);println!("最短的字符串: {}", result);
}

从函数返回引用时,返回类型的生命周期参数要与其中一个参数的生命周期匹配

fn find_smallest<'a>(x: &'a str, y: &str) -> &'a str {let s1 = String::from("hello world");//返回s1的切片,也就是对s1的引用,离开函数后s1被drop,该函数返回了一个指向未知数据的引用s1.as_str()
} //drop s1
fn main() {let x = "apple";let y = "banana";let result = find_smallest(x, y);println!("最短的字符串: {}", result);
}
  • 生命周期在函数方法的参数中:输入生命周期
  • 生命周期在函数方法的返回值中:输出生命周期

生命周期省略规则:

  • 每个引用类型的参数都有自己的生命周期
  • 如果只有1个输入生命周期,那么该生命周期被赋给所有的输出生命周期
  • 如果有多个输入生命周期,但其中一个参数是&self&mut self(仅方法),那么self的生命周期会被赋给所有的输出生命周期
struct Foo<'a> {//可以不标注,在impl里标注data: &'a i32, //每个引用类型的参数都有自己的生命周期,可以不标注
}
struct Foo2 {}
impl<'a> Foo<'a> {//如果只有1个输入生命周期,那么该生命周期被赋给所有的输出生命周期fn new(data: &'a i32) -> Foo<'a> {//这个生命周期会被推断出来,可以不标注Foo { data }}//如果只有1个输入生命周期,那么该生命周期被赋给所有的输出生命周期fn get_data(&self) -> &'a i32 {//这个生命周期会被推断出来,可以不标注self.data}//如果有多个生命周期,但其中一个参数是`&self`或`&mut self`(仅方法),那么`self`的生命周期会被赋给所有的输出生命周期fn modify_data(&mut self, new_data: &'a i32) -> i32 {//可以推断返回值生命周期,但impl生命周期为'a,确保引用有效性,需要标注生命周期self.data = new_data;12 //这个返回值没有意义}//如果有多个生命周期,但其中一个参数是`&self`或`&mut self`(仅方法),那么`self`的生命周期会被赋给所有的输出生命周期fn combine_data1<'b>(&'a self, other: &'a i32) -> &i32 {//可以推断返回值的生命周期,但是要确保引用有效性,需要对参数和函数标注生命周期if *self.data > *other {self.data} else {other}}
}
impl Foo2 {//不满足推断规则需要对参数返回值标注生命周期fn combine_data2<'q>(a: &'q i32, b: &'q i32) -> &'q i32 {if a < b {a} else {b}}
}
fn main() {let x = 5;let y = 10;let mut foo = Foo::new(&x);println!("初始化的值: {}", foo.get_data()); //初始化的值: 5foo.modify_data(&y);println!("修改后的值: {}", foo.get_data()); //修改后的值: 10let combined_data1 = foo.combine_data1(&y);println!("返回最大的: {}", combined_data1); //返回最大的: 10let combine_data2 = Foo2::combine_data2(&x, &y);println!("返回最小的:{}", combine_data2); //返回最小的:5
}

struct字段生命周期

  • struct后面标注
  • impl后标注
  • 这些生命周期是struct类型的一部分

impl块里的生命周期

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

静态生命周期

'static:整个程序的持续时间

  • 所有的字符串字面值都拥有'static生命周期
fn main() {let s1: &'static str = "hello world";//等价于let s2: &str = "hello owrld";
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com