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

溫馨提示×

溫馨提示×

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

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

Rust處理錯誤的方法是什么

發布時間:2023-03-31 17:07:12 來源:億速云 閱讀:347 作者:iii 欄目:開發技術

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

    錯誤處理

    Rust 中的錯誤主要分為兩類:

    • 可恢復錯誤,通常用于從系統全局角度來看可以接受的錯誤,例如處理用戶的訪問、操作等錯誤,這些錯誤只會影響某個用戶自身的操作進程,而不會對系統的全局穩定性產生影響

    • 不可恢復錯誤,剛好相反,該錯誤通常是全局性或者系統性的錯誤,例如數組越界訪問,系統啟動時發生了影響啟動流程的錯誤等等,這些錯誤的影響往往對于系統來說是致命的

    不可恢復錯誤

    不可恢復錯誤通常是非常嚴重的,例如:程序一開始讀取配置文件失敗或者連接數據庫失敗,諸如此類導致程序運行發生致命錯誤的,可以使用不可恢復錯誤。在rust中,觸發不可恢復錯誤使用panic即可。

    觸發panic可以分為被動觸發和主動調用兩種方式。

    被動觸發

    下面是一個被動觸發panic的例子。

    fn main() {
        let v = vec![1, 2, 3];
        v[99];
    }

    這段代碼由于數組越界訪問,導致被動觸發了panic。錯誤信息如下所示:

    thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

    backtrace棧展開

    可以注意到上面的note提示我們在run的時候使用RUST_BACKTRACE=1來進行棧回溯,它包含了函數調用的順序。例如:

     RUST_BACKTRACE=1 cargo run

    執行以后輸出的錯誤如下所示:

    thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5
    stack backtrace:
    0: rust_begin_unwind
              at /rustc/fc594f15669680fa70d255faec3ca3fb507c3405/library/std/src/panicking.rs:575:5
    1: core::panicking::panic_fmt
              at /rustc/fc594f15669680fa70d255faec3ca3fb507c3405/library/core/src/panicking.rs:64:14
    2: core::panicking::panic_bounds_check
              at /rustc/fc594f15669680fa70d255faec3ca3fb507c3405/library/core/src/panicking.rs:147:5
    3: <usize as core::slice::index::SliceIndex<[T]>>::index
              at /rustc/fc594f15669680fa70d255faec3ca3fb507c3405/library/core/src/slice/index.rs:260:10
    4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
              at /rustc/fc594f15669680fa70d255faec3ca3fb507c3405/library/core/src/slice/index.rs:18:9
    5: <alloc::vec::Vec<T,A> as core::ops::index::Index<I>>::index
              at /rustc/fc594f15669680fa70d255faec3ca3fb507c3405/library/alloc/src/vec/mod.rs:2727:9
    6: error_handling::main
              at ./src/main.rs:4:5
    7: core::ops::function::FnOnce::call_once
              at /rustc/fc594f15669680fa70d255faec3ca3fb507c3405/library/core/src/ops/function.rs:507:5
    note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

    最近調用的函數排在列表的最上方。因為咱們的 main 函數基本是最先調用的函數了,所以排在了倒數第二位,還有一個關注點,排在最頂部最后一個調用的函數是 rust_begin_unwind,該函數的目的就是進行棧展開,呈現這些列表信息給我們。

    要獲取到棧回溯信息,你還需要開啟 debug 標志,該標志在使用 cargo run 或者 cargo build 時自動開啟(這兩個操作默認是 Debug 運行方式)。同時,棧展開信息在不同操作系統或者 Rust 版本上也有所不同。

    panic時的兩種終止方式

    當出現 panic! 時,程序提供了兩種方式來處理終止流程:棧展開和直接終止。

    其中,默認的方式就是 棧展開,這意味著 Rust 會回溯棧上數據和函數調用,因此也意味著更多的善后工作,好處是可以給出充分的報錯信息和棧調用信息,便于事后的問題復盤。直接終止,顧名思義,不清理數據就直接退出程序,善后工作交與操作系統來負責。

    對于絕大多數用戶,使用默認選擇是最好的,但是當你關心最終編譯出的二進制可執行文件大小時,那么可以嘗試去使用直接終止的方式,例如下面的配置修改 Cargo.toml 文件,實現在 release 模式下遇到 panic 直接終止:

    [profile.release]
    panic = 'abort'

    主動調用panic

    在某些特殊場景中,開發者想要主動拋出一個異常。rust提供了panic!宏,它可以在你調用時,打印出一個錯誤信息,展開報錯點往前的函數調用堆棧,最后退出程序。一定是不可恢復的錯誤,才調用 panic! 處理,你總不想系統僅僅因為用戶隨便傳入一個非法參數就崩潰吧?所以,只有當你不知道該如何處理時,再去調用 panic!

    fn main() {
        panic!("crash");
    }

    運行后輸出:

    thread 'main' panicked at 'crash', src/main.rs:8:5
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

    它告訴我們,main 函數所在的線程崩潰了,發生的代碼位置是 src/main.rs 中的第 8 行第 5 個字符(去除該行前面的空字符)

    線程panic后程序是否會終止

    如果是 main 線程,則程序會終止,如果是其它子線程,該線程會終止,但是不會影響 main 線程。因此,盡量不要在 main 線程中做太多任務,將這些任務交由子線程去做,就算子線程 panic 也不會導致整個程序的結束。

    Result枚舉類型

    它被定義為如下:

    enum Result<T, E> {
        Ok(T),
        Err(E),
    }

    泛型參數 T 代表成功時存入的正確值的類型,存放方式是 Ok(T),E 代表錯誤時存入的錯誤值,存放方式是 Err(E)。一個實際的例子如下:

    #![allow(unused)]
    use std::fs::File;
    fn main() {
        let f = File::open("hello.txt");
        let f = match f {
            Ok(file) => file,
            Err(error) => {
                panic!("Problem opening the file: {:?}", error)
            },
        };
    }

    代碼很清晰,對打開文件后的 Result<T, E> 類型進行匹配取值,如果是成功,則將 Ok(file) 中存放的的文件句柄 file 賦值給 f,如果失敗,則將 Err(error) 中存放的錯誤信息 error 使用 panic 拋出來,進而結束程序。

    直接 panic 還是過于粗暴,因為實際上 IO 的錯誤有很多種,我們需要對部分錯誤進行特殊處理,而不是所有錯誤都直接崩潰:

    #![allow(unused)]
    use std::fs::File;
    use std::io::ErrorKind;
    fn main() {
        let f = File::open("hello.txt");
        let f = match f {
            Ok(file) => file,
            Err(error) => match error.kind() {
                ErrorKind::NotFound => match File::create("hello.txt") {
                    Ok(fc) => fc,
                    Err(e) => panic!("Problem creating the file: {:?}", e),
                },
                other_error => panic!("Problem opening the file: {:?}", other_error),
            },
        };
    }

    上面代碼在匹配出 error 后,又對 error 進行了詳細的匹配解析,最終結果:

    如果是文件不存在錯誤 ErrorKind::NotFound,就創建文件,這里創建文件File::create 也是返回 Result,因此繼續用 match 對其結果進行處理:創建成功,將新的文件句柄賦值給 f,如果失敗,則 panic

    剩下的錯誤,一律 panic.

    unwrap和expect

    它們的作用就是,如果返回成功,就將 Ok(T) 中的值取出來,如果失敗,就直接 panic。例如:

    use std::fs::File;
    fn main() {
        let f = File::open("hello.txt").unwrap();
    }

    如果hello.txt不存在,則會導致panic;而expect會帶上自定義的錯誤提示信息,相當于重載了錯誤打印的函數:

    use std::fs::File;
    fn main() {
        let f = File::open("hello.txt").expect("Failed to open hello.txt");
    }

    如果hello.txt不存在,那么panic的時候expect會帶上自定義的錯誤提示信息“Failed to open hello.txt”。

    傳播錯誤

    rust提供了錯誤傳遞的方式,以滿足不同的編程風格來處理錯誤。有的人喜歡原地處理,有的人則是需要將錯誤傳遞到上層調用處進行處理。rust提供了?來進行錯誤傳播。例如:

    #![allow(unused)]
    fn main() {
    use std::fs::File;
    use std::io;
    use std::io::Read;
    fn read_username_from_file() -> Result<String, io::Error> {
        let mut f = File::open("hello.txt")?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        Ok(s)
    }
    let res = read_username_from_file();
    dbg!(&res);
    }

    我們在此處進行了錯誤傳遞,當前目錄下不存在hello.txt是,?會把發生的錯誤傳遞到上層,也是就是調用read_username_from_file處,錯誤結果保存在res中。輸出如下所示:

    [src/main.rs:64] &res = Err(
        Os {
            code: 2,
            kind: NotFound,
            message: "No such file or directory",
        },
    )

    詳細的顯示了錯誤信息,包含錯誤碼code,錯誤種類kind,錯誤消息message。?其實是一個宏。當使用 ? 運算符時,如果表達式的結果是一個錯誤值,那么整個函數將立即返回這個錯誤值,否則會將表達式的結果進行包裝并繼續執行函數。?的強大之處在于自動類型提升,例如:

    fn main() {
    fn open_file() -> Result<File, Box<dyn std::error::Error>> {
        let mut f = File::open("hello.txt")?;
        Ok(f)
    }
    let res = open_file();
    dbg!(&res);
    }

    當前目錄下沒有hello.txt時,open會失敗,此時發送的錯誤是std::io::Error 類型,但是 open_file 函數返回的錯誤類型是 std::error::Error 的特征對象。標準庫中定義的 From 特征,該特征有一個方法 from,用于把一個類型轉成另外一個類型,? 可以自動調用該方法,然后進行隱式類型轉換。因此只要函數返回的錯誤 ReturnError 實現了 From<OtherError> 特征,那么 ? 就會自動把 OtherError 轉換為 ReturnError。除此之外,?還可以實現鏈式調用。例如:

    #![allow(unused)]
    fn main() {
    use std::fs::File;
    use std::io;
    use std::io::Read;
    fn read_username_from_file() -> Result<String, io::Error> {
        let mut s = String::new();
        File::open("hello.txt")?.read_to_string(&mut s)?;
        Ok(s)
    }
    }

    確實牛逼,這樣就不用寫一大堆代碼來處理錯誤了。

    ?用于Option返回

    ? 不僅僅可以用于 Result 的傳播,還能用于 Option 的傳播。

    fn main() {
    fn last_char_of_first_line(text: &str) -> Option<char> {
        text.lines().next()?.chars().last()
    }
    let res = last_char_of_first_line("123");
    dbg!(&res);
    }

    如果next返回的是None,那么執行結束,直接返回None,否則接著進行鏈式調用。

    帶返回值的 main 函數

    在了解了 ? 的使用限制后,這段代碼你很容易看出它無法編譯:

    use std::fs::File;
    fn main() {
        let f = File::open("hello.txt")?;
    }

    因為 ? 要求 Result<T, E> 形式的返回值,而 main 函數的返回是 (),怎么辦?實際上 Rust 還支持另外一種形式的 main 函數:

    use std::error::Error;
    use std::fs::File;
    fn main() -> Result<(), Box<dyn Error>> {
        let f = File::open("hello.txt")?;
        Ok(())
    }

    這樣就能使用 ? 提前返回了,同時我們又一次看到了Box<dyn Error> 特征對象,因為 std::error:Error 是 Rust 中抽象層次最高的錯誤,其它標準庫中的錯誤都實現了該特征,因此我們可以用該特征對象代表一切錯誤,就算 main 函數中調用任何標準庫函數發生錯誤,都可以通過 Box<dyn Error>這個特征對象進行返回.

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

    向AI問一下細節

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

    AI

    长海县| 遵义县| 台中市| 汾阳市| 固始县| 龙井市| 九寨沟县| 谷城县| 深圳市| 开封县| 肃北| 临邑县| 新和县| 长乐市| 上思县| 澜沧| 永安市| 新田县| 达孜县| 娱乐| 苏州市| 博兴县| 广德县| 新密市| 漳平市| 南昌市| 鄂尔多斯市| 广丰县| 冀州市| 藁城市| 巫溪县| 公安县| 顺义区| 册亨县| 温州市| 隆尧县| 西林县| 手游| 永顺县| 海晏县| 长垣县|