本文簡要介紹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。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。