"首次提交"

This commit is contained in:
2024-06-08 15:01:12 +08:00
parent 6e0f708d0a
commit 84349a2469
112 changed files with 3272 additions and 0 deletions

181
panic_except/src/main.rs Executable file
View File

@ -0,0 +1,181 @@
use std::{fs::File, io::{self, ErrorKind, Read}, net::IpAddr};
fn main() {
println!("Hello, world!");
/* panic 属于不可恢复的错误 */
//主动触发一个panic
// panic!("crash and burn!")
//在panic的时候控制台会输出很多信息我们可以根据这些信息来获知崩溃的位置。以及调用链
//我们可以设置一些环境变量来决定信息的多少:
// set RUST_BACKTRACE=0 关闭触发异常时的堆栈回溯。仅显示崩溃信息
// set RUST_BACKTRACE=1 打开堆栈回溯
// set RUST_BACKTRACE=full 显示特别详细的堆栈回溯信息
//访问其他代码触发异常 比如这里访问越界。会在vec的代码里面发生panic
/* thread 'main' panicked at src\main.rs:9:23:
index out of bounds: the len is 3 but the index is 99
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
*/
// let var = vec![1,2,3];
// println!("{}", var[99]);
/* 在代码编写中可能会有一些异常的参数值或者状态出现在代码运行时这时候我们应该处理这些值避免进一步蔓延甚至导致panic的发生
处理的方式可能是纠错,自行恢复也有可能是返回错误到更上层让其处理;
也有可能是调用别人提供的功能,别人返回了错误,我们需要处理这些错误。在其他的语言中通常要么是通过一个返回值来判断,
亦或是捕获异常并进行异常的处理但是在RUST中是没有异常的捕获处理方式的。
其通过Result枚举来进行错误的承载和处理
在我们的coding过程中应该按照
1. 如果这个函数必定不会失败。那么返回值就是根据需要的返回值类型
2. 如果这个函数不一定会成功那么返回值就是Result枚举让上层必须处理可能存在的错误
3. 如果函数内部可能会发生错误但是错误一旦出现不可恢复那么应该使用panic进行终止程序
*/
//尝试打开一个文件因为文件不一定会能够打开失败所以这里返回值Result类型我们必须要进行处理
let f = File::open("hello_word.txt");
//这里使用match 表达式进行处理, 保证f一定是可用的
let mut f = match f{
Ok(file) => file,// 成功-> 将文件对象作为match表达式的值进行返回
Err(error) => {
//这里根据错误的类型进行处理。
match error.kind() {
//如果文件不存在则尝试创建一个空文件, 并返回
ErrorKind::NotFound => match File::create("hello_word.txt") {
Ok(file) => file,
Err(error) => panic!("{}", error),//创建失败错误直接panic
}
//其他类型错误直接panic
_ => panic!("{}", error),
}
}
};
/*
从上面可以看出对于可能会失败的接口采用Result的返回值可以使得
调用者必须处理潜在的错误来保证结果是可用的,以及对于错误会强制你进行选择,该如何处理,
比如上面的例子:
对于open失败的情况根据错误的类型进行差异化的处理。直到所有的错误得到正视并处理整个open file的区块才算结束
这种方式可以使得代码的错误处理分支极其完备,减少大量我们认为不会出现的异常错误,而在发生时按照非预期的流程蔓延开来。
当然上面match不断的嵌套是一件很痛苦的处理过程
*/
//如果代码能走到这里说明f一定是可用的状态
let mut buff = String::new();
let _ = f.read_to_string(&mut buff);
println!("file conext is [{}]", buff);
//对比上面的方式,下面这个函数的处理方式就是如果失败了返回到调用者使其进行
//决断确定错误的处理方式避免在函数内进行panic的情况。
match read_username_from_file() {
Ok(name) => println!("name is {}", name),
Err(error) => println!("err happend! {}", error),
}
/* 但是即使是将错误进行传播也需要大量的match 表达式进行匹配并处理,
并且随着函数内部功能的增加,需要处理的失败的代码大大增多,
所以RUST提供了一个专门的运算符 ? 来减少这部分match代码的代码量
使得可以更多的专注在正常的代码逻辑上。
*/
match read_username_from_file1() {
Ok(name) => println!("name is {}", name),
Err(error) => println!("err happend! {}", error),
}
//使用链式调用减少代码,可见如下函数的实现
match read_username_from_file2() {
Ok(name) => println!("name is {}", name),
Err(error) => println!("err happend! {}", error),
}
// 其他的Result处理方式
other_panic();
}
//定义一个函数返回值类型是Result
fn read_username_from_file() -> Result<String, io::Error> {
/* 这里演示了一种传播错误的方法我们通过将函数的返回值定义为Result的方式
当我们在函数的内部实现遇到了一些问题时我们可以将问题传播着了解问题的信息,
并交给他去决定该怎么处理,
*/
let f = File::open("name.txt");
let mut f = match f {
Ok(file) => file,
//如果打开失败了那么就将失败的错误传播回去
Err(error) => return Err(error),
};
let mut buff = String::new();
match f.read_to_string(&mut buff) {
//如果读取失败了则也是返回一个错误
Err(error) => return Err(error),
Ok(size) => {
//如果size为0主动构造一个错误进行返回
if size == 0 {
return Err(io::Error::new(io::ErrorKind::Other, "File is empty"));
}
},
}
Ok(buff)
}
//这是将read_username_from_file使用 ? 运算符进行简化之后的实现
fn read_username_from_file1() -> Result<String, io::Error> {
/* ? 表达式可以使得当错误发生时将错误进行Return
通过这种方式可以大大减少代码量,当然这种语法必须建立在错误的类型都是一致的情况下
如果错误类型不一致或者源错误不能转化为目标错误的话是不能这样使用的,
另外?运算符号只能在Result或者option为返回值的函数才能够使用否则会编译报错
*/
let mut buff = String::new();
let mut f = File::open("name.txt")?;// ? 运算符
let size = f.read_to_string(&mut buff)?;// ? 运算符
if size == 0 {
return Err(io::Error::new(io::ErrorKind::Other, "File is empty"));
}
Ok(buff)
}
//使用链式调用进一步简化代码
fn read_username_from_file2() -> Result<String, io::Error> {
/* 因为使用了?运算符可以保证如果函数可以走到后续的代码说明一定是成功了的
我们就可以肆无忌惮的使用这个结果,
所以可以使用链式调用来进一步缩减中间值的使用而保证一定是安全的
*/
let mut buff = String::new();
if File::open("name.txt")?.read_to_string(&mut buff)? == 0 {
return Err(io::Error::new(io::ErrorKind::Other, "File is empty"))
}
Ok(buff)
}
fn other_panic() {
/* 除了主动的panic宏来进行错误检测后的主动panic。我们还有另外的panic的方式: unwrap 和 except
unwrap可以在结果为Err或者为None的时候发生panic但是他不提供任何自定义的信息
except可以附加一些信息。
那么为什么会需要这两个东西呢而不是通过match表达式检测异常?
1. 因为开发过程中在初期阶段,我们可能没办法完整处理所有的错误,因为有些组件还没准备完成
2. 亦或是这是一个简单程序。不需要那么健壮性,也不需要考虑各种异常。
3. 这就是一个测试程序,目标测试的接口函数就应该不发生任何异常。
当我们在使用一些返回值是Result的函数接口的时候就可以使用这两个方法进行结果的检测和处理
*/
//我们如果知道这一定是不可能会失败的那么我们可以用unwrap来进行结果的提取而不必理会
//比如这里我们明确他是一个常量的合法的IP地址那么就直接使用unwrap来提取即可
let ip:IpAddr = "127.0.0.1".parse().unwrap();
//他和下面这样是一样的效果,但是我们知道不可能失败错误就没必要这么复杂了
// let ip:IpAddr = match "127.0.0.1".parse() {
// Ok(ipaddr) => ipaddr,
// Err(error) => panic!(),
// };
//这是使用except的方式可以添加一些信息
let ip:IpAddr = "127.0.0.1".parse().expect("解析ip地址失败了");
}
/* 可以看出Rust在对于错误的处理的安全上也是及其苛刻的
他不仅要求你知晓你所使用的接口函数可能会存在的错误。
而且也通过语法尽可能的鼓励你使用Result来将错误不隐藏及时传播或者处理
同时为了避免程序陷入为了处理错误而带来的大量异常处理代码,加上了?运算符大大减少了代码量。
*/