rust一些習慣表達方法

學習rust的朋友可能經常看到Result和Option,雖然不一定直接看到他們本身,但是他們的方法還是常見的,比如:xxx.ok().expect(“…”);
這個xxx一般就是某個函數返回的Result類型了,下面就詳細的講解下他們的來源

現在看看rust book裏的那個guess game,有這麼一段:
http://doc.rust-lang.org/book/guessing-game.html

use std::io;

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}

前面幾行都好理解,我們看看10~12行的幾個ok,expect到底是什麼

理解這些內容最好的方法就是看源代碼,不過大家別慌,不是要你從頭開始啃rust所有源碼,只需要有針對性的看就可以了
大家記住下面這個常用的鏈接,這兒可以查到rust的所有源代碼,可以搜索,反應速度非常快:
https://doc.rust-lang.org/std/

1、io::stdin()

現在從頭開始,先看io::stdin()這個是什麼

這個網頁的最上方就是輸入框了,我們一步步來,先輸入 io::stdin
注意:不要加後面括號
下面就是搜索的結果,可以看到第二行就是我們要找的函數(第一行是結構體,我們要找的是函數)
這裏寫圖片描述

點擊進去就可以查看源代碼了,這個函數聲明很簡單,只是返回了一個Stdin的結構體
這裏寫圖片描述

如果想看源代碼就點右上角的那個src,見上圖右上角紅色框

我們現在不需要去看源代碼了,現在看看Stdin的介紹就可以了,看圖上左邊的紅色框裏的Stdin是可以點擊的,點進去然後找到read_line方法:

fn read_line(&mut self, buf: &mut String) -> Result<usize>[−]

Locks this handle and reads a line of input into the specified buffer.

For detailed semantics of this method, see the documentation on BufRead::read_line.

上面是read_line的介紹,我們不去關心他的實現過程了,先看看他返回的類型是:

Result<usize>

2、Result

點進去看看Result的頁面,這個就是這篇blog的重點了

type Result<T> = Result<T, Error>;

A type for results generated by I/O related functions where the Err type is hard-wired to io::Error.

This typedef is generally used to avoid writing out io::Error directly and is otherwise a direct mapping to std::result::Result.

上面的介紹部分說的是io::Result其實是爲了書寫方便定義的,他用io::Error類型替代了std::result::Result<T,Error>裏的Error類型

這樣io::Result比std::result::Result更加具體化了,那麼寫起來也相對簡單了,他只可能返回io::Error類型的錯誤

因爲這兒io::Result只是個類型定義,所以我們要去看std::result::Result的源代碼,搜索過程就不詳述了,具體看源碼:

pub enum Result<T, E> {
    /// Contains the success value
    #[stable(feature = "rust1", since = "1.0.0")]
    Ok(T),

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

上面就是定義了,可以看到他是個enum,有OK和Err類型,分別對應了Result泛型裏的類型T和E,std::result::Result裏並沒有限制E和T的類型,但是io::Result就把E的類型限制成了io::Error,這個大家注意下就好

說了這麼多我們再看看問題

io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

剛纔我們確認了read_line返回的是io::Result<T>類型,那麼ok()函數肯定就是Result的一個方法了,繼續看std::result::Result的方法實現的源代碼:

    pub fn ok(self) -> Option<T> {
        match self {
            Ok(x)  => Some(x),
            Err(_) => None,
        }
    }

3、Option

可以看到ok函數返回的是Option<T>,還是沒有返回我們最終想要的類型T,我們還是先看看ok的代碼吧。

其實這個函數非常簡單,就是一個match,如果沒有出錯用Option::Some把我們要的數據用包裝下返回;如果出錯了就返回Option::None

這樣皮球又提到了Option去了…我們再繼續查Option:

pub enum Option<T> {
    None,
    Some(T),
}

上面就是Option的定義,也是個enum。其中None顧名思義就是“沒有”,他沒有包裝類型T,所以他真的什麼都沒。Some帶來我們的類型T,所以現在目標已經很靜了,只要把T對應的數據弄出來就最終得到了我們要的數據了

繼續看這個代碼,ok()返回的是Option,那麼expect肯定就是Option的方法了

io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

繼續看Option源代碼,看他方法的實現:

    pub fn expect(self, msg: &str) -> T {
        match self {
            Some(val) => val,
            None => panic!("{}", msg),
        }
    }

終於看到想要的返回類型了:T,這個就是我們最終要的數據了,看read_line返回的是io::Result<usize>,所以這兒返回的是一個uszie類型的長度,不過guess game裏並沒有使用他

可以看到這個函數也是個match,如果是Some能匹配了就把他攜帶的數據返回;如果是None類型說明這個Option根本就沒攜帶數據,也就是前面的Result出錯了,所以會調用panic!宏把你傳遞進去的字符串打印出來並且退出程序。

4、其他寫法

現在繞了一大圈終於找到T了,其實ok().expect(…);這種只是偷懶的寫法,出錯了直接打印你的字符串就退出程序了。當然有更偷懶的,看std::result::Result的代碼:

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

那麼代碼就可以寫成下面這樣就可以了:

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

這個是非常不建議的用法,除非你非常肯定read_line不可能出錯

雖然這個read_line結果我們並沒有使用,但是還是需要處理一下,不然Result沒有處理編譯器會給警告的

處理result最直接和直觀的方法就是直接match,不需要通過Option中轉了:

match io::stdin().read_line(&mut guess) {
    Ok(size)=>xxxx,
    Err(e)=>xxx,
}

xxx處換成你自己的代碼就可以了

發佈了116 篇原創文章 · 獲贊 21 · 訪問量 52萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章