rust学习-panic

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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 值是成员 Okunwrap 会返回 Ok 中的值
// 如果 Result 是成员 Errunwrap 会调用 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 中的值程序将继续执行
    // 如果值是 ErrErr 中的值将作为整个函数的返回值
    // 错误值就被传播给了调用者
    //
    // 与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 来告诉代码调用者他需要处理潜在的成功或失败

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6