3.4 rust 异常处理


 

Unrecoverable Errors with panic!

Sometimes, bad things happen in your code, and there’s nothing you can do about it. In these cases, Rust has the panic! macro. When the panic! macro executes, your program will print a failure message, unwind and clean up the stack, and then quit. This most commonly occurs when a bug of some kind has been detected and it’s not clear to the programmer how to handle the error.

如果遇到异常时不进行栈内存释放处理,即遇错程序直接退出,则需要设置panic = 'abort'

Unwinding the Stack or Aborting in Response to a Panic
By default, when a panic occurs, the program starts unwinding, which means Rust walks back up the stack and cleans up the data from each function it encounters. But this walking back and cleanup is a lot of work. The alternative is to immediately abort, which ends the program without cleaning up. Memory that the program was using will then need to be cleaned up by the operating system. If in your project you need to make the resulting binary as small as possible, you can switch from unwinding to aborting upon a panic by adding panic = 'abort' to the appropriate [profile] sections in your Cargo.toml file. For example, if you want to abort on panic in release mode, add this:


[profile.release]
panic = 'abort'

 

panic! 

pub fn p1(){
    panic!(" error !");
}
thread 'main' panicked at ' error !', src/test/pan.rs:5:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
$ export RUST_BACKTRACE=1
$ cargo run 
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/hongyun RUST_BACKTRACE=1`
------------
thread 'main' panicked at ' error !', src/test/pan.rs:5:5
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.46/src/backtrace/libunwind.rs:86
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.46/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src/libstd/sys_common/backtrace.rs:78
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src/libstd/sys_common/backtrace.rs:59
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1076
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1537
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:62
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:49
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:198
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:217
  10: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:526
  11: std::panicking::begin_panic
             at /home/tanpengfei3/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:456
  12: hongyun::test::pan::p1
             at src/test/pan.rs:5
  13: hongyun::main
             at src/main.rs:7
  14: std::rt::lang_start::{{closure}}
             at /home/tanpengfei3/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67
  15: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:52
  16: std::panicking::try::do_call
             at src/libstd/panicking.rs:348
  17: std::panicking::try
             at src/libstd/panicking.rs:325
  18: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  19: std::rt::lang_start_internal
             at src/libstd/rt.rs:51
  20: std::rt::lang_start
             at /home/tanpengfei3/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67
  21: main
  22: __libc_start_main
  23: _start
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

 

程序异常

fn main() {
    let v = vec![1, 2, 3];

    v[99];
}

 

Here, we’re attempting to access the 100th element of our vector (which is at index 99 because indexing starts at zero), but it has only 3 elements. In this situation, Rust will panic. Using [] is supposed to return an element, but if you pass an invalid index, there’s no element that Rust could return here that would be correct.

In C, attempting to read beyond the end of a data structure is undefined behavior. You might get whatever is at the location in memory that would correspond to that element in the data structure, even though the memory doesn’t belong to that structure. This is called a buffer overread and can lead to security vulnerabilities if an attacker is able to manipulate the index in such a way as to read data they shouldn’t be allowed to that is stored after the data structure.

To protect your program from this sort of vulnerability, if you try to read an element at an index that doesn’t exist, Rust will stop execution and refuse to continue.

 

 

Recoverable Errors with Result

#![allow(unused)]
fn main() {
enum Result<T, E> {
    Ok(T),
    Err(E),
}
}

 

 T represents the type of the value that will be returned in a success case within the Ok variant, and E represents the type of the error that will be returned in a failure case within the Err variant. 

注意Result是一个枚举,枚举的值类型是相同的,即Ok,Err两个对象的类型都是Result,在函数参数传值或返回值的时候,会用到这个特性

 

use std::fs::File;
use std::io;
use std::io::Read;

pub fn read_from_file() -> Result<String, io::Error> {
    let f = File::open("/tmp/test2.log");

    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),
    }
}

 

文件读取返回的对象是 std::result::Result,枚举,里面包装两个值,正确的,错误的

In the case where File::open succeeds, the value in the variable f will be an instance of Ok that contains a file handle. In the case where it fails, the value in f will be an instance of Err that contains more information about the kind of error that happened.

 

Let’s look at the return type of the function first: Result<String, io::Error>. This means the function is returning a value of the type Result<T, E> where the generic parameter T has been filled in with the concrete type String and the generic type E has been filled in with the concrete type io::Error. If this function succeeds without any problems, the code that calls this function will receive an Ok value that holds a String—the username that this function read from the file. If this function encounters any problems, the code that calls this function will receive an Err value that holds an instance of io::Error that contains more information about what the problems were.

 如果文件不存在报以下错误就返回,也可以不用return,而是创建该文件

Err(Os { code: 2, kind: NotFound, message: "No such file or directory" })

 

对错误进行分类

use std::fs::File; 
use std::io::ErrorKind;


pub fn open_with_create(){ let fil: String
= "/tmp/hello.txt".to_string(); let f = File::open(&fil); let f = match f { Ok(file) => file, Err(error) => match error.kind() { ErrorKind::NotFound => match File::create(&fil) { Ok(fc) => fc, Err(e) => panic!("Problem creating the file: {:?}", e), }, other_error => { panic!("Problem opening the file: {:?}", other_error) } }, }; }

 

Shortcuts for Panic on Error: unwrap and expect

If the Result value is the Ok variant, unwrap will return the value inside the Ok. If the Result is the Err variant, unwrap will call the panic! 

use std::fs::File;

fn main() {
    let f = File::open("hello.txt").unwrap();
}

 

Another method, expect, which is similar to unwrap, lets us also choose the panic! error message. Using expect instead of unwrap and providing good error messages can convey your intent and make tracking down the source of a panic easier. 

use std::fs::File;

fn main() {
    let f = File::open("hello.txt").expect("Failed to open hello.txt");
}

Because this error message starts with the text we specified, Failed to open hello.txt, it will be easier to find where in the code this error message is coming from. If we use unwrap in multiple places, it can take more time to figure out exactly which unwrap is causing the panic because all unwrap calls that panic print the same message.

当代码或错误比较多的时候,.expect("某某方法报某某异常"),可以快速地定位哪段代码出了问题,而wnwarp的错误大多是一样的。

 

简写一

pub fn read_file1() -> Result<String, io::Error> {
    let f = File::open("/tmp/a.log");

    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut s = String::new();

    f.read_to_string(&mut s)?;
    Ok(s)
}

 

 If the value of the Result is an Ok, the value inside the Ok will get returned from this expression, and the program will continue. If the value is an Err, the Err will be returned from the whole function as if we had used the return keyword so the error value gets propagated to the calling code.

 error values that have the ? operator called on them go through the from function, defined in the From trait in the standard library, which is used to convert errors from one type into another. When the ? operator calls the from function, the error type received is converted into the error type defined in the return type of the current function.

the ? at the end of the File::open call will return the value inside an Ok to the variable f. If an error occurs, the ? operator will return early out of the whole function and give any Err value to the calling code.

 如果发生异常,? 会自动调用一个叫from的方法,把异常的类型进行转换,我们得到的将是一个异常的输出,比如

let res = tools::fil::read_file1();
println!("res:{:?}",res);

如果文件不存在 ,则输出以下内容 

res:Err(Os { code: 2, kind: NotFound, message: "No such file or directory" })

 文件存在的输出

res:Ok("asb\n")

 

Result<String, io::Error> 返回值只有一个,本人学过Java,开始总是下意识地认为这是一个键值对,是两个值,不是的,这是rust、rust、rust!!! 
Result<String, io::Error> 是枚举,之所以有两个类型,是因为枚举的不同值可以是不同的类型(同一类型的枚举,不同的其他类型),rust中Result<String, io::Error>只会返回一个String或io::Error类型的枚举值 

 

简写二

pub fn read_file2() -> Result<String, io::Error> {
    let mut s = String::new();
    File::open("/tmp/a.log")?.read_to_string(&mut s)?;
    Ok(s)
}

We’ve moved the creation of the new String in s to the beginning of the function; that part hasn’t changed. Instead of creating a variable f, we’ve chained the call to read_to_string directly onto the result of File::open("hello.txt")?. We still have a ? at the end of the read_to_string call, and we still return an Ok value containing the username in s when both File::open and read_to_string succeed rather than returning errors.

 

简写三

pub fn read_file3() -> Result<String, io::Error> {
    fs::read_to_string("/tmp/a.log")
}

Reading a file into a string is a fairly common operation, so Rust provides the convenient fs::read_to_string function that opens the file, creates a new String, reads the contents of the file, puts the contents into that String, and returns it.

 

从指定文件中搜索一个字符串

该例子包含了一些错误处理的方法技巧

#![allow(unused)]

use std::env;
use std::error::Error;
use std::fs;

#[derive(Debug)]
pub struct Config {
    pub query: String,
    pub filename: String,
    pub case_sensitive: bool,
}

impl Config {
    //成功返回一个struct实例,异常则返回一个字符串切片
    pub fn new(args: &[String]) -> Result<Config, &str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }

        let query = args[1].clone();
        let filename = args[2].clone();

        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();

        Ok(Config {
            query,
            filename,
            case_sensitive,
        })
    }
}

fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let contents = fs::read_to_string(config.filename)?;

    let results = if config.case_sensitive {
        search(&config.query, &contents)
    } else {
        search_case_insensitive(&config.query, &contents)
    };

    for line in results {
        println!("{}", line);
    }

    Ok(())
}

fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let mut results = Vec::new();

    for line in contents.lines() {
        if line.contains(query) {
            results.push(line);
        }
    }

    results
}

fn search_case_insensitive<'a>(
    query: &str,
    contents: &'a str,
) -> Vec<&'a str> {
    let query = query.to_lowercase();
    let mut results = Vec::new();

    for line in contents.lines() {
        if line.to_lowercase().contains(&query) {
            results.push(line);
        }
    }

    results
}

//从Result中将struct实例取出来
fn get_config(result: Result<Config, &str>) -> Config {
    match result {
        Ok(n)  => n,
        Err(e) => panic!("over"),
    }
}

pub fn test(){
    let args = [String::from("search"),String::from("bb"),String::from("/tmp/logs/aa.log")];
    let cfg_res = Config::new(&args);
    let cfg = get_config(cfg_res);
    print!("{:#?}\n",cfg);
    print!("{:?}\n",cfg.case_sensitive);
    run(cfg);
}

调用test方法输出

------------------------
Config {
    query: "bb",
    filename: "/tmp/logs/aa.log",
    case_sensitive: true,
}
true
bb to bb
ai@aisty:/tmp/logs$ cat /tmp/logs/aa.log 
aa
aa
bb to bb

 

pub fn new(args: &[String]) -> Result<Config, &str>

new方法初始化一个struct实例,返回一个Result,是个枚举,异常时则是返回一个字符串(可以看到具体发生了什么错);

 

fn get_config(result: Result<Config, &str>) -> Config

get_config是从Result取出需要的数据,因Result实际上是一个枚举,不是Config

 

env::var: 从当前的线程中读取变量,返回的是一个Result,

官方对env::var的解释

pub fn var<K: AsRef<OsStr>>(key: K) -> Result<String, VarError>

如果我在程序中定义了CASE_INSENSITIVE变量,那么is_err()就是false了

let CASE_INSENSITIVE = false;
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();

这说明Result是一个is_err()方法,可以判断返回是不是异常;

Box用于在堆上创建数据,有Box<T>,Box::new(T)等用法,Box<dyn Error>则是不必去关心具体错误的细节,只要错误继承了std::error::Error就可以了,比如

fn test2() -> Result<(), Box<dyn Error>>{

    //这里写了很多代码,不确定是否就有错,错误返回就写上一个Box<dyn Error>
    let a = 5;
    let b = a - 5;
    let c = 3/b;
    // let c = 3/(b+1);
    print!("{}\n",c);

    Ok(())
}

pub fn test3(){
    let a = test2();
    print!("result:{}\n",a.is_err());

    let b = match a {
        Err(e) => format!("{:#?}",e),
        Ok(()) => String::from("")
    };
    print!("Box<dyn Error>---------:{}",b);
}

()就是空元组,相当于占个位置,Ok中的()要与Result的第一个参数()对上,意思就是如果程序无异常,返回个空元组

 

 &'a str

这是为了表示从方法参数开始,到方法返回值,这个范围的变量,生命周期相同。

 

下面这篇文章的错误处理方法非常详细,但由于太详细了,不适合初学者看,初学者需要先掌握常用的几种先用着就可以了

【译】Rust 中的错误处理


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM