99re热视频这里只精品,久久久天堂国产精品女人,国产av一区二区三区,久久久精品成人免费看片,99久久精品免费看国产一区二区三区

錯誤處理

2018-08-12 22:03 更新

錯誤處理

不管是人是鼠,即使最如意的安排設(shè)計,結(jié)局也往往會出其不意。 《致老鼠》 羅伯特·彭斯

有時候,事情會出乎意料的發(fā)生錯誤。重要的是要提前想好應(yīng)對錯誤的方法。Rust 有豐富的支持錯誤處理方法來應(yīng)對可能(老實說:將會)發(fā)生在您的程序中的錯誤。

主要有兩種類型的錯誤可能發(fā)生在你的程序中:故障和異常。讓我們談?wù)剝烧咧g的區(qū)別,然后討論如何處理它們。接著,將討論如何將故障升級為異常。

故障 VS 異常

Rust 使用兩個術(shù)語來區(qū)分兩種形式的錯誤:故障和異常。故障是可以用某種方式中恢復(fù)的錯誤。異常是一種不能恢復(fù)的錯誤。

我們說的 ”恢復(fù)“ 是什么意思?嗯,在大多數(shù)情況下,指的是預(yù)計一個錯誤的可能性。例如,考慮 parse 函數(shù):

"5".parse();

這個方法將一個字符串轉(zhuǎn)換成另一種類型。但因為它是一個字符串,你不能確保轉(zhuǎn)換工作正常執(zhí)行。例如,執(zhí)行如下的轉(zhuǎn)換會得到什么?

"hello5world".parse();

這是行不通的。所以我們知道,這個函數(shù)只會對一些特定的輸入才能正常工作。這是預(yù)期行為。我們稱這種錯誤為故障。

另一方面,有時,有意想不到的錯誤,或者我們不能恢復(fù)它。一個典型的例子是一個斷言:

assert!(x == 5);

我們使用 assert! 說明參數(shù)是正確的。如果這不是正確的,那么這個斷言就是錯誤的。錯誤的話,我們不就能繼續(xù)在當前狀態(tài)往下執(zhí)行了。另一個例子是使用 unreachable!() 宏:

enum Event {
    NewRelease;
}

fn probability(_: &Event) -> f64 {
    // real implementation would be more complex, of course
    0.95
}

fn descriptive_probability(event: Event) -> &'static str {
    match probability(&event) {
        1.00 => "certain",
        0.00 => "impossible",
        0.00 ... 0.25 => "very unlikely",
        0.25 ... 0.50 => "unlikely",
        0.50 ... 0.75 => "likely",
        0.75 ... 1.00 => "very likely",
    }
}

fn main() {
    std::io::println(descriptive_probability(NewRelease));
}

它將會輸出如下的錯誤:

error: non-exhaustive patterns: `_` not covered [E0004]

盡管我們已經(jīng)涵蓋所有我們知道的可能情況情況,但是 Rust 不清楚。Rust 不知道概率是 0.0 和 1.0 之間。所以我們添加另一個例子:

use Event::NewRelease;

enum Event {
    NewRelease,
}

fn probability(_: &Event) -> f64 {
    // real implementation would be more complex, of course
    0.95
}

fn descriptive_probability(event: Event) -> &'static str {
    match probability(&event) {
        1.00 => "certain",
        0.00 => "impossible",
        0.00 ... 0.25 => "very unlikely",
        0.25 ... 0.50 => "unlikely",
        0.50 ... 0.75 => "likely",
        0.75 ... 1.00 => "very likely",
        _ => unreachable!()
    }
}

fn main() {
    println!("{}", descriptive_probability(NewRelease));
}

我們不應(yīng)該得到 _ 情況,所以我們使用 unreachable!() 宏來說明這個。unreachable!() 比結(jié)果給出了不同于 Result 類型的錯誤。Rust 稱這些類型的錯誤為異常。

利用 Option 和 Result 處理錯誤

表明一個函數(shù)可能會失敗的最簡單方法是使用 option< T >類型。例如,字符串 find 方法試圖找到字符串的一個模式串,并返回 Option:

let s = "foo";

assert_eq!(s.find('f'), Some(0));
assert_eq!(s.find('z'), None);

對這些簡單的情況下是可以的,但是在故障的情況下并不會給我們提供很多的信息。如果我們想知道為什么函數(shù)發(fā)生了故障,怎么辦?為此,我們可以使用 Result<T, E>類型。它看起來像這樣:

enum Result<T,E> {
    ok(T),
    Err(E)
}

這個枚舉類型由 Rust 本身提供,所以你不需要在你的代碼中定義就可以使用它。Ok(T) 變量代表著成功執(zhí)行,Err(E) 變量代表著執(zhí)行失敗。推薦在大多數(shù)情況下返回一個 Result而不是一個 Option 變量。

如下是一個使用 Result 的例子:

#[derive(Debug)]
enum Version { Version1, Version2 }

#[derive(Debug)]
enum ParseError { InvalidHeaderLength, InvalidVersion }

fn parse_version(header: &[u8]) -> Result<Version, ParseError> {
    if header.len() < 1 {
        return Err(ParseError::InvalidHeaderLength);
    }
    match header[0] {
        1 => Ok(Version::Version1),
        2 => Ok(Version::Version2),
        _ => Err(ParseError::InvalidVersion)
    }
}

let version = parse_version(&[1, 2, 3, 4]);
match version {
    Ok(v) => {
        println!("working with version: {:?}", v);
    }
    Err(e) => {
        println!("error parsing header: {:?}", e);
    }
}

這個函數(shù)使用枚舉類型變量 ParseError 列舉各種可能發(fā)生的錯誤。

調(diào)試特點就是讓我們使用 {:?} 格式來打印該枚舉變量的值。

遇到 panic!類型的不可恢復(fù)錯誤

遇到為意料的和不可恢復(fù)的的錯誤時,宏 panic! 會引起異常。這將崩潰當前線程,并給出一個錯誤:

panic!("boom");

當你運行時會輸出:

thred '<main>' panicked at 'boom', hello.rs:2

因為這些類型的情況相對較少見,很少使用恐慌。

升級故障為異常

在某些情況下,即使一個函數(shù)可能發(fā)生故障,我們?nèi)韵胍阉斪饕粋€異常對待。例如,io::stdin().read_line(&mut buff) 函數(shù)在讀取某一行時出現(xiàn)錯誤會返回 Result< usize > 變量。這使我們能夠處理它并可能從錯誤中恢復(fù)。

如果我們不想處理這個錯誤,而寧愿只是中止程序,那么我們可以使用 unwrap() 方法:

io::stdin().read_line(&mut buffer).unwrap();

如果 Result 變量值是 Err,unwrap() 方法將會產(chǎn)生調(diào)用 panic!,輸出異常。這基本上是說“給我變量的值,如果出現(xiàn)錯誤,就讓程序崩潰?!斑@相對于匹配錯誤并試圖恢復(fù)的方式可靠性較低,但也大大縮短執(zhí)行時間。有時,只是崩潰程序是合理的。

有另一種方式比 unwrap() 方法好一點:

let mut buffer = String::new();
let input = io::stdin().read_line(&mut buffer)
                       .ok()
                       .expect("Failed to read line");

ok() 方法將 Result 轉(zhuǎn)換成一個 Option,并 expect() 和 unwrap() 方法做的事是一樣的,不同點在于它需要一個參數(shù),用來輸出提示信息。這個消息被傳遞到底層的 panic!,如果代碼出現(xiàn)錯誤,它提供一個較好的錯誤消息展示方式。

使用 try!

當編寫的代碼調(diào)用很多的返回 Result 類型的函數(shù)時,錯誤處理就變得比較冗長。try! 宏利用堆棧對產(chǎn)生的錯誤進行引用從而隱藏具體的細節(jié)。

將如下的代碼:

use std::fs::File;
use std::io;
use std::io::prelude::*;

struct Info {
    name: String,
    age: i32,
    rating: i32,
}

fn write_info(info: &Info) -> io::Result<()> {
    let mut file = File::create("my_best_friends.txt").unwrap();

    if let Err(e) = writeln!(&mut file, "name: {}", info.name) {
        return Err(e)
    }
    if let Err(e) = writeln!(&mut file, "age: {}", info.age) {
        return Err(e)
    }
    if let Err(e) = writeln!(&mut file, "rating: {}", info.rating) {
        return Err(e)
    }

    return Ok(());
}

替換成:

use std::fs::File;
use std::io;
use std::io::prelude::*;

struct Info {
    name: String,
    age: i32,
    rating: i32,
}

fn write_info(info: &Info) -> io::Result<()> {
    let mut file = try!(File::create("my_best_friends.txt"));

    try!(writeln!(&mut file, "name: {}", info.name));
    try!(writeln!(&mut file, "age: {}", info.age));
    try!(writeln!(&mut file, "rating: {}", info.rating));

    return Ok(());
}

用 try! 封裝一個表達式,在成功執(zhí)行時給 Result 賦值為 Ok,否則賦值為 Err,在這種情況下,在函數(shù)未執(zhí)行完成之前就會返回 Err 值。

值得注意的是,你只能在返回 Result 的函數(shù)中使用 try!,這意味著您不能在 main() 中使用 try!,因為 main() 不返回任何值。

try! 利用 From 來確定在錯誤的情況下返回的值。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號