本文簡要介紹rust語言中 Primitive Type never 的用法。
! 類型,也稱為“never”。
! 表示永遠不會解析為任何值的計算類型。例如, exit 函數 fn exit(code: i32) -> ! 退出進程而不返回,因此返回 ! 。
break 、 continue 和 return 表達式也有類型 ! 。例如,我們可以這樣寫:
#![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 的函數,其中 ! 沒有 Trait 的 impl 不能作為它們唯一可能的代碼路徑發散。換句話說,它們不能從每個代碼路徑返回!。例如,此代碼無法編譯:
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 needs_drop用法及代碼示例
- Rust null_mut用法及代碼示例
- Rust null用法及代碼示例
- Rust UdpSocket.set_multicast_loop_v6用法及代碼示例
- Rust i64.overflowing_add_unsigned用法及代碼示例
- Rust Box.downcast用法及代碼示例
- Rust BTreeMap.last_key_value用法及代碼示例
- Rust str.make_ascii_uppercase用法及代碼示例
- Rust u128.checked_pow用法及代碼示例
- Rust usize.wrapping_mul用法及代碼示例
- Rust AtomicU8.fetch_sub用法及代碼示例
- Rust PanicInfo.payload用法及代碼示例
- Rust MaybeUninit.assume_init_mut用法及代碼示例
- Rust String.try_reserve用法及代碼示例
- Rust Mutex.new用法及代碼示例
- Rust f32.exp用法及代碼示例
- Rust Result.unwrap_or_else用法及代碼示例
- Rust slice.sort_unstable_by_key用法及代碼示例
- Rust Formatter.precision用法及代碼示例
- Rust i128.log2用法及代碼示例
- Rust OsStr.to_ascii_uppercase用法及代碼示例
- Rust f32.hypot用法及代碼示例
- Rust RefCell.try_borrow_unguarded用法及代碼示例
- Rust i16.log10用法及代碼示例
- Rust LowerExp用法及代碼示例
注:本文由純淨天空篩選整理自rust-lang.org大神的英文原創作品 Primitive Type never。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。
