Rust可恢復的錯誤
可恢復的錯誤是那些完全停止程序並不嚴重的錯誤。可以處理的錯誤稱爲可恢復錯誤。
它由Result <T,E>
表示。 結果<T,E>
是枚舉,由兩個變體組成,即OK <T>
和Err <E>
,它描述了可能的錯誤。
OK <T>
:’T’是一種值,它在成功情況下時返回OK
變量,這是一個預期的結果。Err <E>
:’E’是一種錯誤,它在失敗情況下時返回ERR
變量,這是意想不到的結果。
Enum Result<T,E>
{
OK<T>,
Err<E>,
}
- 在上面的例子中,
Result
是枚舉類型,OK <T>&Err <E>
是枚舉類型的變體,其中'T'
和'E'
是泛型類型參數。 -
'T'
是一種值,它將在成功的情況下返回,而'E'
是一種錯誤,它將在失敗的情況下返回。 -
Result
包含泛型類型參數,因此在成功和失敗值可能不同的許多不同情況下使用標準庫中定義的結果類型和函數。
下面來看一個返回Result
值的簡單示例:
use std::fs::File;
fn main()
{
let f:u32 = File::open("vector.txt");
}
執行上面示例代碼,得到以下結果 -
在上面的示例中,Rust編譯器顯示該類型不匹配。 'f'
是u32
類型,而File::open
返回Result <T,E>
類型。 上面的輸出顯示成功值的類型是std::fs::File
,錯誤值的類型是std::io::Error
。
注意:
-
File::open
返回類型是成功值或失敗值。 如果File::open
成功,則返回文件句柄,如果File::open
失敗,則返回錯誤值。 結果枚舉提供此信息。 - 如果
File::open
成功,則f
將具有包含文件句柄的OK
變體,如果File::open
失敗,則f將具有包含與錯誤相關的信息的Err
變體。
匹配表達式以處理結果變體
下面來看看一個匹配表達式的簡單示例:
use std::fs::File;
fn main()
{
let f = File::open("vector.txt");
match f
{
Ok(file) => file,
Err(error) => {
panic!("There was a problem opening the file: {:?}", error)
},
};
}
執行上面示例代碼,得到以下結果 -
程序說明
- 在上面的示例中,可以直接訪問枚舉變體,而無需在
OK
和Err
變體之前使用Result::
。 - 如果結果正常,則返回文件並將其存儲在
'f'
變量中。 在匹配之後,可以在文件中執行讀取或寫入操作。 - 匹配對
Err
值起作用。 如果Result
返回Error
值,那麼panic!
運行並停止程序的執行。
出錯時 Error:unwrap()
- 結果
<T,E>
有許多方法可以提供各種任務。 其中一種方法是unwrap()
方法。unwrap()
方法是匹配表達式的快捷方法。unwrap()
方法和匹配表達式的工作方式是一樣的。 - 如果
Result
值是OK
變量,則unwrap()
方法返回OK
變量的值。 - 如果
Result
值是Err
變量,那麼unwrap()
方法會調用panic!
宏。
下面來看一個簡單的示例:
use std::fs::File;
fn main()
{
File::open("hello.txt").unwrap();
}
執行上面示例代碼,得到以下結果:
在上面的例子中,unwrap()
方法會自動調用panic!
宏顯示錯誤信息。
Error: expect()
-
expect()
方法的行爲方式與unwrap()
方法相同,即兩種方法都會引起panic!
宏顯示錯誤信息。 -
expect()
和unwrap()
方法之間的區別在於,錯誤消息作爲參數傳遞給expect()
方法,而unwrap()
方法不包含任何參數。 因此,可以說expect()
方法可以跟蹤panic!
的來源更容易。
下面看看一個簡單示例 -
use std::fs::File;
fn main()
{
File::open("hello.txt").expect("Not able to find the file hello.txt");
}
執行上面示例代碼,得到以下結果 -
在上面的輸出中,錯誤消息顯示在程序中指定的輸出屏幕上,即「無法找到文件hello.txt」
,這會更容易找到錯誤代碼的位置。 如果包含多個unwrap()
方法,那麼很難找到unwrap()
方法引發panic!
顯示所有錯誤消息。
傳播錯誤
傳播錯誤是一種將錯誤從一個函數轉發到另一個函數的機制。 錯誤傳播到調用函數,其中有更多信息可用,以便可以處理錯誤。 假設有一個名爲‘a.txt’ 的文件,它包含文本「yiibai」 。想要創建一個程序來讀取文件,先參考下面一個簡單的例子:
use std::io;
use std::io::Read;
use std::fs::File;
fn main()
{
let a = read_username_from_file();
print!("{:?}",a);
}
fn read_username_from_file() -> Result<String, io::Error>
{
let f = File::open("a.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
程序說明 -
-
read_username_from_file()
函數返回Result <T,E>
類型的值,其中'T'
是String
類型,'E'
是io
類型:Error
。 - 如果函數成功,則返回一個包含
String
的OK
值,如果函數失敗,則返回Err
值。 - 函數中首先調用
File::open
函數。 如果File::open
函數失敗,那麼匹配的第二個句柄將返回Err
值,如果File::open
函數成功,則它將文件句柄的值存儲在變量f
中。 - 如果
File::open
函數成功,那麼創建一個String
的變量。 如果read_to_string()
方法成功,則返回文件的文本,否則返回錯誤信息。 - 假設我們有一個名爲‘a.text’ 的外部文件,幷包含文本「yiibai」 。 因此,該程序讀取文件‘a.text’ 並顯示文件的內容。
傳播錯誤的捷徑:?
操作符
使用?
運算符減少了代碼的長度。?
運算符是匹配表達式的替換意味着?
運算符的工作方式與匹配表達式的工作方式相同。 假設有一個名爲 a.txt 的文件,它包含文本 - 「yiibai」
。想要創建一個程序來對該文件執行讀取操作。
看看下面一個簡單的例子。
use std::io;
use std::io::Read;
use std::fs::File;
fn main()
{
let a = read_username_from_file();
print!("{:?}",a);
}
fn read_username_from_file() -> Result<String, io::Error>
{
let mut f = File::open("a.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
在上面的例子中,?
運算符是在Result
值類型之前使用。 如果Result
爲OK
,則返回OK
變體的值,如果Result
爲Err
,則返回錯誤信息。
?運算符和匹配表達式的區別
- 使用
?
運算符的錯誤通過’from’函數移動,’from’函數在標準庫中的from trait
中定義。 - 當
?
運算符調用’from’函數,然後此函數將錯誤類型轉換爲當前函數的返回類型中定義的錯誤類型。 - 如果沒有錯誤發生,那麼
?
運算符任何函數末尾的運算符返回OK
的值,如果發生錯誤,則返回Err
的值。 - 它使函數的實現更簡單。
鏈方法在 ?運算符之後調用
甚至可以通過在?
運算符之後使用鏈接方法調用來縮短程序的代碼。
下面來看一個簡單的例子:
use std::io;
use std::io::Read;
use std::fs::File;
fn main()
{
let a = read_username_from_file();
print!("{:?}",a);
}
fn read_username_from_file() -> Result<String, io::Error>
{
let mut s = String::new();
File::open("a.txt")?.read_to_string(&mut s)?;
Ok(s)
}
程序說明
在上面的例子中,將read_to_string()
的調用鏈接到File::open("a.txt")?
的調用結果。如果兩個函數(即read_to_string()
和File::open("a.txt")
成功,則返回OK
值,否則返回錯誤值。
?運算符的限制
?
運算符只能在返回Result
類型值的函數中使用。 ?
運算符與匹配表達式的工作方式類似。 匹配表達式僅適用於Result
返回類型。
下面通過一個簡單的例子來理解這一點。
use std::fs::File;
fn main()
{
let f = File::open("a.txt")?;
}