Rust使用kind進(jìn)行異常處理(錯(cuò)誤的分類(lèi)與傳遞)
前言
Rust 有一套獨(dú)特的處理異常情況的機(jī)制,它并不像其它語(yǔ)言中的 try 機(jī)制那樣簡(jiǎn)單。
在Rust 中的錯(cuò)誤分為兩大類(lèi):可恢復(fù)錯(cuò)誤和不可恢復(fù)錯(cuò)誤。大多數(shù)編程語(yǔ)言用 Exception (異常)類(lèi)來(lái)表示錯(cuò)誤。在 Rust 中沒(méi)有 Exception。對(duì)于可恢復(fù)錯(cuò)誤用 Result<T, E> 類(lèi)來(lái)處理,對(duì)于不可恢復(fù)錯(cuò)誤使用 panic! 宏來(lái)處理。
1、不可恢復(fù)錯(cuò)誤
- 由編程中無(wú)法解決的邏輯錯(cuò)誤導(dǎo)致的,例如訪問(wèn)數(shù)組末尾以外的位置。
1.1、panic! 宏的使用
宏的使用較為簡(jiǎn)單,讓我們來(lái)看一個(gè)具體例子:
fn main() {
panic!("Error occured");
println!("Hello, rust");
}運(yùn)行結(jié)果:

很顯然,程序并不能如約運(yùn)行到 println!("Hello, rust") ,而是在 panic! 宏被調(diào)用時(shí)停止了運(yùn)行,不可恢復(fù)的錯(cuò)誤一定會(huì)導(dǎo)致程序受到致命的打擊而終止運(yùn)行。
1.2、通過(guò) Powershell命令行分析錯(cuò)誤原因
我們來(lái)分析一下終端命令行中的報(bào)錯(cuò)信息:
thread 'main' panicked at 'Error occured', src\main.rs:2:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
- 第一行輸出了 panic! 宏調(diào)用的位置以及其輸出的錯(cuò)誤信息
- 第二行是一句提示,翻譯成中文就是"通過(guò)
RUST_BACKTRACE=full環(huán)境變量運(yùn)行以顯示回溯"。
接下來(lái)看一下回溯(backtrace)信息:
stack backtrace:
0: std::panicking::begin_panic_handler
at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:584
1: core::panicking::panic_fmt
at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\core\src\panicking.rs:142
2: error_deal::main
at .\src\main.rs:2
3: core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3\library\core\src\ops\function.rs:248
回溯是不可恢復(fù)錯(cuò)誤的另一種處理方式,它會(huì)展開(kāi)運(yùn)行的棧并輸出所有的信息,然后程序依然會(huì)退出。通過(guò)大量的輸出信息,我們可以找到我們編寫(xiě)的 panic! 宏觸發(fā)的錯(cuò)誤。
2、可恢復(fù)的錯(cuò)誤
- 如果訪問(wèn)一個(gè)文件失敗,有可能是因?yàn)樗诒徽加?,是正常的,我們可以通過(guò)等待來(lái)解決。
2.1、Rustlt<T,E>枚舉類(lèi)的使用
此概念十分類(lèi)似于 Java 編程語(yǔ)言中的異常,而在 C 語(yǔ)言中我們就常常將函數(shù)返回值設(shè)置成整數(shù)來(lái)表達(dá)函數(shù)遇到的錯(cuò)誤,在 Rust 中通過(guò) Result<T, E> 枚舉類(lèi)作返回值來(lái)進(jìn)行異常表達(dá):
enum Result<T, E> {
Ok(T),
Err(E),
}//T的類(lèi)型不定,相當(dāng)于C++中模板的寫(xiě)法我們知道
enum常常與match配合使用,當(dāng)匹配到OK時(shí)就會(huì)執(zhí)行相應(yīng)代碼。
在 Rust 標(biāo)準(zhǔn)庫(kù)中可能產(chǎn)生異常的函數(shù)的返回值都是 Result 類(lèi)型。
例如:當(dāng)我們嘗試打開(kāi)一個(gè)文件時(shí):
use std::fs::File;
fn main() {
let fp = File::open("hello_rust.txt");
match fp {
Ok(file) => {
println!("File opened successfully.");
},
Err(err) => {
println!("Failed to open the file.");
}
}
}//OK里的參數(shù)file是File類(lèi)型,相當(dāng)于填充了枚舉里的T類(lèi)型如果
hello_rust.txt文件不存在,會(huì)打印 Failed to open the file.
當(dāng)然,我們?cè)诿杜e類(lèi)章節(jié)講到的 if let 模式匹配語(yǔ)法可以簡(jiǎn)化 match 語(yǔ)法塊:
use std::fs::File;
fn main() {
let fp = File::open("hello_rust.txt");
if let Ok(file) = fp {
println!("File opened successfully.");
} else {
println!("Failed to open the file.");
}
}2.2、Result 類(lèi)的unwrap() 和 expect(message: &str) 方法
將一個(gè)可恢復(fù)錯(cuò)誤按不可恢復(fù)錯(cuò)誤處理
舉個(gè)例子:
use std::fs::File;
fn main() {
let fp1 = File::open("hello_rust.txt").unwrap();
let fp2 = File::open("hello_rust.txt").expect("Failed to open.");
}
- 這段程序相當(dāng)于在 Result 為
Err時(shí)調(diào)用 panic!宏 - 兩者的區(qū)別在于
expect能夠向 panic! 宏發(fā)送一段指定的錯(cuò)誤信息 panic!宏是不可恢復(fù)錯(cuò)誤,這樣就完成了轉(zhuǎn)變
3、可恢復(fù)的錯(cuò)誤的傳遞
之前所講的是接收到錯(cuò)誤的處理方式,接下來(lái)講講怎么把錯(cuò)誤信息傳遞出去
我們先來(lái)編寫(xiě)一個(gè)函數(shù):
fn f(i: i32) -> Result<i32, bool> {
if i >= 0 {
Ok(i)
}
else {
Err(false)
}
}
fn main() {
let r = f(10000);
if let Ok(v) = r {
println!("Ok: f(-1) = {}", v);
} else {
println!("Err");
}
}//運(yùn)行結(jié)果:Ok: f(-1) = 10000
這里
r的結(jié)果是f函數(shù)返回的ok(10000),經(jīng)過(guò)if let模式匹配后v的值為10000
這段程序中函數(shù) f 是錯(cuò)誤的根源,現(xiàn)在我們?cè)賹?xiě)一個(gè)傳遞錯(cuò)誤的函數(shù) g :
fn g(i: i32) -> Result<i32, bool> {
let t = f(i);
return match t {
Ok(i) => Ok(i),
Err(b) => Err(b)
};
}
函數(shù) g 傳遞了函數(shù) f 可能出現(xiàn)的錯(cuò)誤,這樣寫(xiě)有些冗長(zhǎng),Rust 中可以在 Result 對(duì)象后添加 ? 操作符將同類(lèi)的 Err 直接傳遞出去:
fn f(i: i32) -> Result<i32, bool> {
if i >= 0 { Ok(i) }
else { Err(false) }
}
fn g(i: i32) -> Result<i32, bool> {
let t = f(i)?;
Ok(t) // 因?yàn)榇_定 t 不是 Err, t 在這里已經(jīng)推導(dǎo)出是 i32 類(lèi)型
}
fn main() {
let r = g(10000);
if let Ok(v) = r {
println!("Ok: g(10000) = {}", v);
} else {
println!("Err");
}
}//運(yùn)行結(jié)果:Ok: g(10000) = 10000
?符的實(shí)際作用是將 Result 類(lèi)非異常的值直接取出,如果有異常就將異常 Result 返回出去。所以? 符僅用于返回值類(lèi)型為 Result<T, E> 的函數(shù),且其中E類(lèi)型必須和?所處理的 Result 的 E 類(lèi)型一致。
4、結(jié)合kind方法處理異常
雖然前面提到Rust 異常不像其他語(yǔ)言這么簡(jiǎn)單,但這并不意味著 Rust 實(shí)現(xiàn)不了:我們完全可以把 try 塊在獨(dú)立的函數(shù)中實(shí)現(xiàn),將所有的異常都傳遞出去解決。
實(shí)際上這才是一個(gè)分化良好的程序應(yīng)當(dāng)遵循的編程方法:應(yīng)該注重獨(dú)立功能的完整性。
但是這樣需要判斷 Result 的 Err 類(lèi)型,獲取 Err 類(lèi)型的函數(shù)是 kind()
做一個(gè)打開(kāi)文件的實(shí)例:
use std::io;
use std::io::Read;
use std::fs::File;
fn read_text_from_file(path: &str) -> Result<String, io::Error> {
let mut f = File::open(path)?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main() {
let str_file = read_text_from_file("hello_rust.txt");
match str_file {
Ok(s) => println!("{}", s),
Err(e) => {
match e.kind() {
io::ErrorKind::NotFound => {
println!("No such file");
},
_ => {
println!("Cannot read the file");
}
}
}
}
}//這里我沒(méi)有創(chuàng)建hello_rust.txt文件,因此運(yùn)行結(jié)果為:No such file
代碼解釋?zhuān)?/p>
- 使用
read_text_from_file()函數(shù)將文件打開(kāi)的結(jié)果傳給了str_file變量 - 這里并不存在
hello_rust.txt,因此File::open(path)?不會(huì)打開(kāi)文件,異常會(huì)存到f中 f.read_to_string(&mut s)?并不能讀出文件內(nèi)容,ok(s)無(wú)內(nèi)容- 通過(guò)分析,分支會(huì)執(zhí)行
Err(e)的代碼塊,使用e.kind()得到了錯(cuò)誤類(lèi)型并再次進(jìn)行match分支 - 如果是
NotFound錯(cuò)誤就會(huì)打印No such file - 其他情錯(cuò)誤均提示Cannot read the file
到此這篇關(guān)于Rust指南錯(cuò)誤的分類(lèi)與傳遞|使用kind進(jìn)行異常處理的文章就介紹到這了,更多相關(guān)Rust錯(cuò)誤處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何在Rust中處理命令行參數(shù)和環(huán)境變量
在本章節(jié)中, 我們探討了Rust處理命令行參數(shù)的常見(jiàn)的兩種方式和處理環(huán)境變量的兩種常見(jiàn)方式,感興趣的朋友一起看看吧2023-12-12
使用vscode配置Rust運(yùn)行環(huán)境全過(guò)程
VS Code對(duì)Rust有著較完備的支持,這篇文章主要給大家介紹了關(guān)于使用vscode配置Rust運(yùn)行環(huán)境的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06
詳解在Rust語(yǔ)言中如何聲明可變的static類(lèi)型變量
在Rust中,可以使用lazy_static宏來(lái)聲明可變的靜態(tài)變量,lazy_static是一個(gè)用于聲明延遲求值靜態(tài)變量的宏,本文將通過(guò)一個(gè)簡(jiǎn)單的例子,演示如何使用?lazy_static?宏來(lái)聲明一個(gè)可變的靜態(tài)變量,需要的朋友可以參考下2023-08-08
Rust動(dòng)態(tài)數(shù)組Vec基本概念及用法
Rust中的Vec是一種動(dòng)態(tài)數(shù)組,它可以在運(yùn)行時(shí)自動(dòng)調(diào)整大小,本文主要介紹了Rust動(dòng)態(tài)數(shù)組Vec基本概念及用法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
如何使用bindgen將C語(yǔ)言頭文件轉(zhuǎn)換為Rust接口代碼
這篇文章主要介紹了使用bindgen將C語(yǔ)言頭文件轉(zhuǎn)換為Rust接口代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01

