rust学习-panic

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 来告诉代码调用者他需要处理潜在的成功或失败

相关推荐
yuwinter12 分钟前
鸿蒙HarmonyOS学习笔记(8)
笔记·学习
Ricciflows44 分钟前
MIT线性代数教材:Linear Algebra and Its Applications
人工智能·学习·线性代数·机器学习·数学建模·矩阵
计科土狗1 小时前
离散数学第二章笔记
学习
SomeB1oody1 小时前
【Rust自学】6.3. 控制流运算符-match
开发语言·前端·rust
undeflined1 小时前
vite + vue3 + tailwind 启动之后报错
开发语言·后端·rust
美式小田2 小时前
Cadence学习笔记 12 PCB初始化设置
笔记·嵌入式硬件·学习·cadence
席万里2 小时前
【MySQL学习笔记】关于索引
笔记·学习·mysql
深蓝海拓3 小时前
使用sam进行零样本、零学习的分割实践
人工智能·深度学习·学习·目标检测·计算机视觉
滴_咕噜咕噜3 小时前
学习笔记(prism--视频【WPF-prism核心教程】)--待更新
笔记·学习·wpf
ghostwritten3 小时前
学习 Python 编程的规则与风格指南
python·学习