panic!
panic示例
use std::fs::File;
fn main() {
// 使用 Result 类型来处理潜在的错误部分中的Result 枚举
// enum Result<T, E> {
// Ok(T),
// Err(E),
// }
//
// File::open 函数的返回值类型是 Result<T, E>
// T 放入了成功值的类型 std::fs::File,它是一个文件句柄
// E 被用在失败值上时 E 的类型是 std::io::Error
let f = File::open("hello.txt");
// 如果按照如下方式使用,则报错
// let f: u32 = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => {
panic!("Problem opening the file: {:?}", error)
},
};
}
嵌套match
use std::fs::File;
use std::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,
// io::Error 是一个标准库的结构体。它有一个返回 io::ErrorKind 值的 kind 方法
Err(e) => panic!("Problem creating the file: {:?}", e),
},
other_error => panic!("Problem opening the file: {:?}", other_error),
},
};
}
消除match
use std::fs::File;
use std::io::ErrorKind;
// 为了消除大量match的写法
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!("Problem creating the file: {:?}", error);
})
} else {
panic!("Problem opening the file: {:?}", error);
}
});
}
// Result<T, E> 类型定义了很多辅助方法来处理各种情况
// unwrap,似于match 语句
// 如果 Result 值是成员 Ok,unwrap 会返回 Ok 中的值
// 如果 Result 是成员 Err,unwrap 会调用 panic!
use std::fs::File;
fn main() {
let f = File::open("hello.txt").unwrap();
}
expect:选择 panic! 的错误信息
use std::fs::File;
fn main() {
let f = File::open("hello.txt").expect("Failed to open hello.txt");
}
错误透传
use std::io;
use std::io::Read;
use std::fs::File;
// 返回一个包含用户名的 Ok 值,或一个包含 io::Error 的 Err 值
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
// 用match的原因:当 Err 时不再调用 panic!
// 提早返回并将 File::open 返回的错误值作为函数的错误返回值传递给调用者
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),
// 不需要显式调用 return,因为这是函数最后一个表达式
Err(e) => Err(e),
}
}
fn main() {
let result = read_username_from_file();
match result {
Ok(info) => {
println!("info={:?}", info);
},
Err(e) => {
println!("err={:?}", e);
}
}
}
// 文件不存在时,打印结果如需啊
// err=Os { code: 2, kind: NotFound, message: "No such file or directory" }
错误透传简写
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
// Result 值之后的 ? 被定义为与处理 Result 值的 match 表达式有完全相同的工作方式
// 如果 Result 的值是 Ok,这个表达式将会返回 Ok 中的值,程序将继续执行
// 如果值是 Err,Err 中的值将作为整个函数的返回值
// 错误值就被传播给了调用者
//
// 与match的不同
// ? 运算符所使用的错误值被传递给了 from 函数,它定义于标准库的 From trait 中
// 其用来将错误从一种类型转换为另一种类型
// 当 ? 运算符调用 from 函数时,收到的错误类型被转换为由当前函数返回类型所指定的错误类型
// 只要每一个错误类型都实现 from 函数来定义如何将自身转换为返回的错误类型,? 运算符会自动处理这些转换
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main() {
let result = read_username_from_file();
match result {
Ok(info) => {
println!("info={:?}", info);
},
Err(e) => {
println!("err={:?}", e);
}
}
}
use std::io;
use std::io::Read;
use std::fs::File;
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)
}
fn main() {
let result = read_username_from_file();
match result {
Ok(info) => {
println!("info={:?}", info);
},
Err(e) => {
println!("err={:?}", e);
}
}
}
use std::io;
use std::fs;
// Rust 提供了名为 fs::read_to_string 的函数
// 它会打开文件、新建一个 String、读取文件的内容,并将内容放入 String,接着返回它
fn read_username_from_file() -> Result<String, io::Error> {
fs::read_to_string("hello.txt")
}
fn main() {
let result = read_username_from_file();
match result {
Ok(info) => {
println!("info={:?}", info);
},
Err(e) => {
println!("err={:?}", e);
}
}
}
? 运算符可被用于返回值类型为 Result 的函数
use std::fs::File;
fn main() {
let f = File::open("hello.txt")?;
}
// 编译报错,遇到如下问题
// the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
// the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
当期望在不返回 Result 的函数中调用其他返回 Result 的函数时使用 ?
有两种方法修复这个问题:
(1)将函数返回值类型修改为 Result<T, E>
(2)通过合适的方法使用 match 或 Result 的方法之一来处理 Result<T, E>
use std::error::Error;
use std::fs::File;
// Box<dyn Error> 被称为 "trait 对象"(trait object)
// Box<dyn Error> 为使用 ? 时 main 允许返回的 "任何类型的错误"
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("hello.txt")?;
Ok(())
}
// main 函数是特殊的,其必须返回什么类型是有限制的
// main 函数的一个有效的返回值是 (),同时出于方便,另一个有效的返回值是 Result<T, E>
不使用--release
当不使用 --release 参数运行 cargo build 或 cargo run 时,debug 标识会默认启用
fn main() {
let v = vec![1, 2, 3];
v[99];
}
// 使用RUST_BACKTRACE=1运行cargo run
RUST_BACKTRACE=1 cargo run cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `/Users/hello/hello_cargo/target/debug/hello_cargo cargo run`
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5
stack backtrace:
0: rust_begin_unwind
at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/std/src/panicking.rs:578:5
1: core::panicking::panic_fmt
at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/panicking.rs:67:14
2: core::panicking::panic_bounds_check
at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/panicking.rs:162:5
3: <usize as core::slice::index::SliceIndex<[T]>>::index
at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/slice/index.rs:261:10
4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/slice/index.rs:19:9
5: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index
at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/alloc/src/vec/mod.rs:2691:9
6: hello_cargo::main
at ./main.rs:4:5
7: core::ops::function::FnOnce::call_once
at /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
使用--release
如果需要项目的最终二进制文件越小越好,panic 时通过在 Cargo.toml 的 [profile] 部分增加 panic = 'abort',可以由展开切换为终止。如果想要在release模式中 panic 时直接终止:
[profile.release]
panic = 'abort'
当执行这个宏时,程序会打印出一个错误信息,展开并清理栈数据
fn main() {
panic!("crash and burn");
}
release % ls -l
-rwxr-xr-x 1 staff 477536 7 14 12:15 hello_cargo
debug % ls -l
-rwxr-xr-x 1 staff 483016 7 14 12:13 hello_cargo
hello_cargo % ./target/debug/hello_cargo
thread 'main' panicked at 'crash and burn', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
hello_cargo % ./target/release/hello_cargo
thread 'main' panicked at 'crash and burn', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
zsh: abort ./target/release/hello_cargo
其他示例
pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}
Guess {
value
}
}
pub fn value(&self) -> i32 {
self.value
}
}
总结
(1)当项目最终准备好发布时,可以使用 cargo build --release 来优化编译项目。这会在 target/release 而不是 target/debug 下生成可执行文件。这些优化可以让 Rust 代码运行的更快,不过启用这些优化也需要消耗更长的编译时间
(2)使用 Result 来告诉代码调用者他需要处理潜在的成功或失败