當前位置: 首頁>>編程示例 >>用法及示例精選 >>正文


Rust never用法及代碼示例

本文簡要介紹rust語言中 Primitive Type never 的用法。

! 類型,也稱為“never”。

! 表示永遠不會解析為任何值的計算類型。例如, exit 函數 fn exit(code: i32) -> ! 退出進程而不返回,因此返回 !

breakcontinuereturn 表達式也有類型 ! 。例如,我們可以這樣寫:

#![feature(never_type)]
let x: ! = {
    return 123
};

雖然 let 在這裏毫無意義,但它說明了 ! 的含義。由於 x 從未被賦值(因為 return 從整個函數返回),所以 x 可以被賦予類型 ! 。我們也可以用 panic! 或 never-ending loop 替換 return 123,並且此代碼仍然有效。

! 的更實際用法在此代碼中:

let num: u32 = match get_a_number() {
    Some(num) => num,
    None => break,
};

兩個匹配臂都必須產生類型為 u32 的值,但由於 break 根本不會產生值,我們知道它永遠不會產生不是 u32 的值。這說明了 ! 類型的另一種行為 - 類型為 ! 的表達式將強製轉換為任何其他類型。

! 和泛型

萬無一失的錯誤

您將看到顯式使用! 的主要位置是在通用代碼中。考慮 FromStr 特征:

trait FromStr: Sized {
    type Err;
    fn from_str(s: &str) -> Result<Self, Self::Err>;
}

在為 String 實現此特征時,我們需要為 Err 選擇一個類型。並且由於將字符串轉換為字符串永遠不會導致錯誤,因此適當的類型是 ! 。 (目前實際使用的類型是沒有變體的枚舉,但這隻是因為 ! 是在以後添加到 Rust 中的,並且將來可能會更改。)如果 Err 類型為 !由於某種原因,我們必須調用 String::from_str ,結果將是 Result<String, !> ,我們可以像這樣解包:

#![feature(exhaustive_patterns)]
use std::str::FromStr;
let Ok(s) = String::from_str("hello");

由於 Err 變體包含 ! ,因此它永遠不會發生。如果存在exhaustive_patterns 特征,這意味著我們可以通過使用 Ok 變體來徹底匹配 Result<T, !> 。這說明了 ! 的另一種行為 - 它可用於 “delete” 來自泛型類型(如 Result )的某些枚舉變體。

無限循環

盡管Result<T, !>對於消除錯誤非常有用,!也可用於刪除成功。如果我們想到Result<T, !>因為“如果這個函數返回,它就沒有錯誤”,我們得到一個非常直觀的想法Result<!, E>同樣:如果函數返回,它出錯了。

例如,考慮一個簡單的 Web 服務器的情況,可以簡化為:

loop {
    let (client, request) = get_request().expect("disconnected");
    let response = request.process();
    response.send(client);
}

目前,這並不理想,因為每當我們無法獲得新連接時,我們都會感到Panics。相反,我們希望跟蹤此錯誤,如下所示:

loop {
    match get_request() {
        Err(err) => break err,
        Ok((client, request)) => {
            let response = request.process();
            response.send(client);
        },
    }
}

現在,當服務器斷開連接時,我們會以錯誤退出循環而不是Panics。雖然簡單地返回錯誤可能很直觀,但我們可能希望將其包裝在 Result<!, E> 中:

fn server_loop() -> Result<!, ConnectionError> {
    loop {
        let (client, request) = get_request()?;
        let response = request.process();
        response.send(client);
    }
}

現在,我們可以使用 ? 而不是 match ,並且返回類型更有意義:如果循環停止,則意味著發生了錯誤。我們甚至不必將循環包裝在 Ok 中,因為 ! 會自動強製轉換為 Result<!, ConnectionError>

! 和特征

在編寫自己的特征時,隻要有明顯的 impl 而不是 panic!! 就應該有一個 impl 。原因是返回 impl Trait 的函數,其中 ! 沒有 Traitimpl 不能作為它們唯一可能的代碼路徑發散。換句話說,它們不能從每個代碼路徑返回!。例如,此代碼無法編譯:

use std::ops::Add;

fn foo() -> impl Add<u32> {
    unimplemented!()
}

但是這段代碼確實:

use std::ops::Add;

fn foo() -> impl Add<u32> {
    if true {
        unimplemented!()
    } else {
        0
    }
}

原因是,在第一個示例中,! 可以強製轉換為許多可能的類型,因為許多類型實現了 Add<u32> 。但是,在第二個示例中, else 分支返回 0 ,編譯器從返回類型推斷其類型為 u32 。由於 u32 是具體類型,因此 ! 可以並且將會被強製轉換為它。有關 ! 這個怪癖的更多信息,請參閱問題 #36375

然而,事實證明,大多數特征都可以有一個 impl 用於 ! 。以 Debug 為例:

#![feature(never_type)]
impl Debug for ! {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        *self
    }
}

我們再次使用 ! 強製轉換為任何其他類型的能力,在本例中為 fmt::Result 。由於此方法將 &! 作為參數,我們知道它永遠不會被調用(因為沒有類型為 ! 的值可供調用)。編寫 *self 本質上是告訴編譯器“我們知道這段代碼永遠無法運行,所以隻需將整個函數體視為具有類型 fmt::Result ”。在為 ! 實現特征時,可以大量使用此模式。通常,任何僅具有采用 self 參數的方法的特征都應該具有這樣的 impl。

另一方麵,不適合實施的一個特征是 Default

trait Default {
    fn default() -> Self;
}

由於! 沒有值,它也沒有默認值。確實,我們可以為此編寫一個簡單的Panics的impl,但對於任何類型都是如此(我們可以通過 default() Panics來為(例如) File impl Default。)

相關用法


注:本文由純淨天空篩選整理自rust-lang.org大神的英文原創作品 Primitive Type never。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。