Rust中的Result枚舉


Result枚舉在Rust中是使用頻率極高的一個類型,常用於函數的返回值定義,其源碼如下:

#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
#[must_use = "this `Result` may be an `Err` variant, which should be handled"]
#[rustc_diagnostic_item = "result_type"]
#[stable(feature = "rust1", since = "1.0.0")]
pub enum Result<T, E> {
    /// Contains the success value
    #[lang = "Ok"]
    #[stable(feature = "rust1", since = "1.0.0")]
    Ok(#[stable(feature = "rust1", since = "1.0.0")] T),

    /// Contains the error value
    #[lang = "Err"]
    #[stable(feature = "rust1", since = "1.0.0")]
    Err(#[stable(feature = "rust1", since = "1.0.0")] E),
}

拋開一堆#開頭的trait不看,核心就是Ok及Err這2個泛型成員。

 

先來看一個基本示例:

    let result_ok: Result<String, String> = Result::Ok(String::from("success"));
    let result = match result_ok {
        Result::Ok(o) => o,
        Result::Err(e) => e,
    };
    println!("{}", result);

這里定義了一個"成功"的Result,然后使用模式匹配對其進行處理,如果是Ok的,取出Ok的值,否則取出Err的值。這類簡單重復的判斷,經常會用到,rust封裝了1個等效的方法:unwrap,其內部實現如下:

    pub fn unwrap(self) -> T {
        match self {
            Ok(t) => t,
            Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e),
        }
    }

其實就是模式匹配,取出Ok或Err的值。所以前面這個示例,可以改寫成:

    let result_ok: Result<String, String> = Result::Ok(String::from("success"));
    // let result = match result_ok {
    //     Result::Ok(o) => o,
    //     Result::Err(e) => e,
    // };
    let result = result_ok.unwrap();
    println!("{}", result);

unwrap源碼中的unwrap_failed繼續追下去的話,可以看到:

fn unwrap_failed(msg: &str, error: &dyn fmt::Debug) -> ! {
    panic!("{}: {:?}", msg, error)
}

調用了panic方法,這意味着如果Result返回的是Err,則程序會崩潰,可以試一把:

 

如果Err發生時不希望程序崩潰,可以使用unwrap_or()

    let result_fail: Result<String, String> = Result::Err(String::from("failure"));
    let result = result_fail.unwrap_or(String::from("err occur"));
    println!("{}", result);

unwrap_or可以傳入一個default缺省的錯誤值,上面這段將輸出“err occur”。但這樣一來,就把原始的錯誤信息failure給丟失了! 不用擔心,rust早考慮到了:

    let result_fail: Result<String, String> = Result::Err(String::from("failure"));
    let result = result_fail.unwrap_or_else(|e|e);
    println!("{}", result);

使用unwrap_or_else傳入1個閉包匿名函數,可以隨心所欲的對原始錯誤進行處理,這里我們啥也沒干,|e|e,表示原樣返回。

Result枚舉還提供了其它一些常用方法,參見上圖,有興趣的同學,可以研究下源碼。

最后來看一個稍微復雜點的示例:在當前目錄下,打開hello.txt文件,如果該文件不存在,則自動創建一個空的hello.txt。

use core::panic;
use std::fs::File;
use std::io::{ErrorKind};

fn main() {
    let file_name = String::from("hello.txt");

    //第1層match
    match File::open(&file_name) {
        Ok(file) => file,
        //第2層match
        Err(error) => match error.kind() {
            //第3層match
            ErrorKind::NotFound => match File::create(&file_name) {
                Ok(fc) => fc,
                Err(e) => panic!("Error creating file:{:?}", e),
            },
            oe => panic!("Error opening the file:{:?}", oe),
        },
    };
}

用了3層模式匹配(match套娃),看上去比較原始,如果不喜歡這種match寫法,可以用今天學到的知識,換成相對“正常點”的寫法:

    File::open(&file_name).unwrap_or_else(|e| {
        if e.kind() == ErrorKind::NotFound {
            File::create(&file_name).unwrap_or_else(|e| {
                panic!("Error creating file:{:?}", e);
            })
        } else {
            panic!("Error opening file:{:?}", e)
        }
    });

Rust程序員可能會寫得更簡短:

    File::open(&file_name).unwrap_or_else(|e| match e.kind() {
        ErrorKind::NotFound => {
            File::create(&file_name).unwrap_or_else(|e| panic!("Error creating file:{:?}", e))
        }
        _ => panic!("Error opening file:{:?}", e)
    });


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM