中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Rust錯誤處理有哪些

發布時間:2021-10-12 15:46:20 來源:億速云 閱讀:205 作者:iii 欄目:編程語言

這篇文章主要介紹“Rust錯誤處理有哪些”,在日常操作中,相信很多人在Rust錯誤處理有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Rust錯誤處理有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

Rust錯誤處理有哪些

錯誤處理是編程語言中很重要的一個方面。目前,錯誤處理的方式分為兩類,第一類是以C語言為首的基于返回值的錯誤處理方案,第二類是以Java語言為首的基于異常的錯誤處理方案。也可以從發生了錯誤是否可恢復來進行分類,例如,C語言中對可恢復的錯誤會使用錯誤碼返回值,對不可恢復的錯誤會直接調用exit來退出程序;Java的異常體系分為ExceptionError,分別對應可恢復錯誤和不可恢復錯誤。在Rust中,錯誤處理的方案和C語言類似,但更加完善好用:對于不可恢復錯誤,使用panic來處理,使得程序直接退出并可輸出相關信息;對于可恢復錯誤,使用OptionResult來對返回值進行封裝,表達能力更強。

不可恢復錯誤

panic簡介

對于不可恢復錯誤,Rust提供了panic機制來使得程序迅速崩潰,并報告相應的出錯信息。panic出現的場景一般是:如果繼續執行下去就會有極其嚴重的內存安全問題,這種時候讓程序繼續執行導致的危害比崩潰更嚴重。舉個例子:

fn main() {
    let v = vec![1, 2, 3];
    println!("{:?}", v[6]);
}

對于上面的程序,數組v有三個元素,但索引值是6,所以運行后程序會崩潰并報以下錯誤:

thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 6', src/main.rs:176:22
stack backtrace:
   函數調用棧...
panic實現機制

在Rust中,panic的實現機制有兩種方式:

  • unwind方式:發生panic時,會一層一層地退出函數調用棧,棧內的局部變量還可以正常析構。

  • abort方式:發生panic時,直接退出整個程序。

默認情況下編譯器使用unwind方式,函數調用棧信息可以幫助我們快速定位發生panic的第一現場;但某些嵌入式系統因資源不足而只能選擇abort方式,可以通過rustc -C panic=abort test.rs方式指定。

在Rust中,通過unwind方式實現的panic,其內部實現方式基本與C++的異常是一樣的。Rust提供了一些工具函數,可以像try-catch機制那樣讓用戶在代碼中終止棧展開,例如:

fn main() {
    std::panic::catch_unwind(|| {
        let v = vec![1, 2, 3];
        println!("{:?}", v[6]);
        println!("interrupted"); // 沒有輸出
    })
    .ok();

    println!("continue"); // 正常輸出
}

運行程序可以發現,println!("interrupted");語句沒有執行,因此在上一條語句出發了panic,這個函數調用棧開始銷毀,但std::panic::catch_unwind阻止了調用棧的繼續展開,因此println!("continue");得以正常執行。

需要注意的是,不要像try-catch那樣使用catch_unwind來進行流程控制,Rust更推薦基于返回值的錯誤處理機制,因為既然發生panic了,就讓程序越早崩潰越好,這有利于調試bug,而使用catch_unwind會讓錯誤暫時被壓制,從而讓錯誤傳遞到其他位置,導致不容易找到程序崩潰的第一現場。catch_unwind主要用于以下兩種情況:

  • 在FFI的場景下,若C語言調用了Rust的函數,在Rust內部出現了panic,如果這個panic在Rust內部沒處理好,直接扔到C代碼中去,會導致產生“未定義行為”。

  • 某些高級抽象機制需要阻止棧展開,例如線程池。如果一個線程中出現了panic,我們只希望把這個線程關閉,而不是將整個線程池拖下水。

可恢復錯誤

基本錯誤處理

對于可恢復的錯誤,Rust中提供了基于返回值的方案,主要基于Option<T>Result<T, E>類型。Option<T>代表返回值要么是空要么是非空,Result<T, E>代表返回值要么是正常值的要么錯誤值。它們的定義如下:

pub enum Option<T> {
    /// No value
    None,
    /// Some value `T`
    Some(#[stable(feature = "rust1", since = "1.0.0")] T),
}

pub enum Result<T, E> {
    /// Contains the success value
    Ok(#[stable(feature = "rust1", since = "1.0.0")] T),

    /// Contains the error value
    Err(#[stable(feature = "rust1", since = "1.0.0")] E),
}

我們來看一個標準庫中對Result<T, E>的典型用法,FromStr中的from_str方法可以通過字符串構造出當前類型的實例,但可能會構造失敗。標準庫中針對bool類型實現了這個trait,正常情況返回bool類型的值,異常情況返回ParseBoolError類型的值:

pub trait FromStr: Sized {
    /// The associated error which can be returned from parsing.
    type Err;

    fn from_str(s: &str) -> Result<Self, Self::Err>;
}

impl FromStr for bool {
    type Err = ParseBoolError;

    fn from_str(s: &str) -> Result<bool, ParseBoolError> {
        match s {
            "true" => Ok(true),
            "false" => Ok(false),
            _ => Err(ParseBoolError { _priv: () }),
        }
    }
}

我們再來看一個標準庫中對Option<T>的典型用法,Iteratornext方法要么返回下一個元素,要么無元素可返回,因此使用Option<T>非常合適。

#[must_use = "iterators are lazy and do nothing unless consumed"]
pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    ...
}

Option<T>類型解決了許多編程語言中存在的空指針問題。空指針這個設計在加入編程語言時沒有經過深思熟慮,而只是因為易于實現而已。空指針最大的問題在于,它違背了類型系統的規定。類型規定了數據可能的取值范圍,規定了在這些值上可能的操作,也規定了這些數據代表的含義,還規定了這些數據的存儲方式。但是,一個普通的指針和一個空指針,哪怕它們是同樣的類型,做同樣的操作,所得到的結果是不同的。因此,并不能說空指針和普通指針是同一個類型,空指針在類型系統上打開了一個缺口,引入了一個必須在運行期特殊處理的值,它讓編譯器的類型檢查在此失去了意義。對此,Rust的解決方案是把空指針null從一個值上升為一個類型,用enum類型的Option<T>None來代表空指針,而Rust中的enum要求在使用時必須對enum的每一種可能性都進行處理,因此強迫程序員必須考慮到Option<T>None的情形。C/C++中也增添了類似的設計,但由于前向兼容的問題,無法強制使用,因此其作用也就弱化了很多。

問號運算符

Rust中提供了問號運算符?語法糖來簡化Result<T, E>Option<T>的使用,問號運算符的意思是,如果結果是Err,則提前返回,否則繼續執行。?對應著std::ops::Try這個trait,編譯器會把expr?這個表達式自動轉換為以下語義:

match Try::into_result(expr) {
    Ok(V) => v,
    Err(e) => return Try::from_error(From::from(e)),
}

標準庫中已經為Result<T, E>Option<T>兩個類型實現了Try

impl<T> ops::Try for Option<T> {
    type Ok = T;
    type Error = NoneError;

    fn into_result(self) -> Result<T, NoneError> {
        self.ok_or(NoneError)
    }

    fn from_ok(v: T) -> Self {
        Some(v)
    }

    fn from_error(_: NoneError) -> Self {
        None
    }
}

impl<T> ops::Try for Option<T> {
    type Ok = T;
    type Error = NoneError;

    fn into_result(self) -> Result<T, NoneError> {
        self.ok_or(NoneError)
    }

    fn from_ok(v: T) -> Self {
        Some(v)
    }

    fn from_error(_: NoneError) -> Self {
        None
    }
}

可以看到,對于Result類型,執行問號運算符時,如果碰到Err,則調用Fromtrait做類型轉換,然后中斷當前邏輯提前返回。

需要注意的是,問號運算符的引入給main函數帶來了挑戰,因為問號運算符要求函數返回值是Result類型,而main函數是fn() -> ()類型,解決這個問題的辦法就是修改main函數的簽名類型,但這樣又會破壞舊代碼。Rust最終的解決方案是引入了一個trait:

pub trait Termination {
    /// Is called to get the representation of the value as status code.
    /// This status code is returned to the operating system.
    fn report(self) -> i32;
}

impl Termination for () {
    #[inline]
    fn report(self) -> i32 {
        ExitCode::SUCCESS.report()
    }
}

impl<E: fmt::Debug> Termination for Result<(), E> {
    fn report(self) -> i32 {
        match self {
            Ok(()) => ().report(),
            Err(err) => Err::<!, _>(err).report(),
        }
    }
}

impl Termination for ! {
    fn report(self) -> i32 {
        self
    }
}

impl<E: fmt::Debug> Termination for Result<!, E> {
    fn report(self) -> i32 {
        let Err(err) = self;
        eprintln!("Error: {:?}", err);
        ExitCode::FAILURE.report()
    }
}

impl Termination for ExitCode {
    #[inline]
    fn report(self) -> i32 {
        self.0.as_i32()
    }
}

main函數的簽名就對應地改成了fn<T: Termination>() -> T,標準庫為Result類型、()類型等都實現了這個trait,從而這些類型都可以作為main函數的返回類型了。

到此,關于“Rust錯誤處理有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

炉霍县| 柯坪县| 泽州县| 密山市| 卓尼县| 芷江| 永兴县| 汶川县| 天台县| 岳普湖县| 定结县| 文成县| 景宁| 金寨县| 长武县| 洪洞县| 古丈县| 疏附县| 抚宁县| 江阴市| 宁远县| 文昌市| 安康市| 奇台县| 新乐市| 井陉县| 报价| 图木舒克市| 遂宁市| 六盘水市| 普安县| 聂荣县| 大足县| 夹江县| 韶关市| 芒康县| 临泉县| 和林格尔县| 新竹县| 兴山县| 嘉兴市|