W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
ch09-01-unrecoverable-errors-with-panic.md
commit 199ca99926f232ee7f581a917eada4b65ff21754
突然有一天,代碼出問題了,而你對此束手無策。對于這種情況,Rust 有 panic!
宏。當執(zhí)行這個宏時,程序會打印出一個錯誤信息,展開并清理棧數(shù)據(jù),然后接著退出。出現(xiàn)這種情況的場景通常是檢測到一些類型的 bug,而且程序員并不清楚該如何處理它。
對應(yīng) panic 時的棧展開或終止
當出現(xiàn) panic 時,程序默認會開始 展開(unwinding),這意味著 Rust 會回溯棧并清理它遇到的每一個函數(shù)的數(shù)據(jù),不過這個回溯并清理的過程有很多工作。另一種選擇是直接 終止(abort),這會不清理數(shù)據(jù)就退出程序。那么程序所使用的內(nèi)存需要由操作系統(tǒng)來清理。如果你需要項目的最終二進制文件越小越好,panic 時通過在 Cargo.toml 的
[profile]
部分增加panic = 'abort'
,可以由展開切換為終止。例如,如果你想要在release模式中 panic 時直接終止:
[profile.release] panic = 'abort'
讓我們在一個簡單的程序中調(diào)用 panic!
:
文件名: src/main.rs
fn main() {
panic!("crash and burn");
}
運行程序?qū)霈F(xiàn)類似這樣的輸出:
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/panic`
thread 'main' panicked at 'crash and burn', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
最后兩行包含 panic!
調(diào)用造成的錯誤信息。第一行顯示了 panic 提供的信息并指明了源碼中 panic 出現(xiàn)的位置:src/main.rs:2:5 表明這是 src/main.rs 文件的第二行第五個字符。
在這個例子中,被指明的那一行是我們代碼的一部分,而且查看這一行的話就會發(fā)現(xiàn) panic!
宏的調(diào)用。在其他情況下,panic!
可能會出現(xiàn)在我們的代碼所調(diào)用的代碼中。錯誤信息報告的文件名和行號可能指向別人代碼中的 panic!
宏調(diào)用,而不是我們代碼中最終導(dǎo)致 panic!
的那一行。我們可以使用 panic!
被調(diào)用的函數(shù)的 backtrace 來尋找代碼中出問題的地方。下面我們會詳細介紹 backtrace 是什么。
讓我們來看看另一個因為我們代碼中的 bug 引起的別的庫中 panic!
的例子,而不是直接的宏調(diào)用。示例 9-1 有一些嘗試通過索引訪問 vector 中元素的例子:
文件名: src/main.rs
fn main() {
panic!("crash and burn");
}
運行程序?qū)霈F(xiàn)類似這樣的輸出:
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/panic`
thread 'main' panicked at 'crash and burn', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
最后兩行包含 panic!
調(diào)用造成的錯誤信息。第一行顯示了 panic 提供的信息并指明了源碼中 panic 出現(xiàn)的位置:src/main.rs:2:5 表明這是 src/main.rs 文件的第二行第五個字符。
在這個例子中,被指明的那一行是我們代碼的一部分,而且查看這一行的話就會發(fā)現(xiàn) panic!
宏的調(diào)用。在其他情況下,panic!
可能會出現(xiàn)在我們的代碼所調(diào)用的代碼中。錯誤信息報告的文件名和行號可能指向別人代碼中的 panic!
宏調(diào)用,而不是我們代碼中最終導(dǎo)致 panic!
的那一行。我們可以使用 panic!
被調(diào)用的函數(shù)的 backtrace 來尋找代碼中出問題的地方。下面我們會詳細介紹 backtrace 是什么。
讓我們來看看另一個因為我們代碼中的 bug 引起的別的庫中 panic!
的例子,而不是直接的宏調(diào)用。示例 9-1 有一些嘗試通過索引訪問 vector 中元素的例子:
文件名: src/main.rs
fn main() {
let v = vec![1, 2, 3];
v[99];
}
示例 9-1:嘗試訪問超越 vector 結(jié)尾的元素,這會造成 panic!
這里嘗試訪問 vector 的第一百個元素(這里的索引是 99 因為索引從 0 開始),不過它只有三個元素。這種情況下 Rust 會 panic。[]
應(yīng)當返回一個元素,不過如果傳遞了一個無效索引,就沒有可供 Rust 返回的正確的元素。
C 語言中,嘗試讀取數(shù)據(jù)結(jié)構(gòu)之后的值是未定義行為(undefined behavior)。你會得到任何對應(yīng)數(shù)據(jù)結(jié)構(gòu)中這個元素的內(nèi)存位置的值,甚至是這些內(nèi)存并不屬于這個數(shù)據(jù)結(jié)構(gòu)的情況。這被稱為 緩沖區(qū)溢出(buffer overread),并可能會導(dǎo)致安全漏洞,比如攻擊者可以像這樣操作索引來讀取儲存在數(shù)據(jù)結(jié)構(gòu)之后不被允許的數(shù)據(jù)。
為了保護程序遠離這類漏洞,如果嘗試讀取一個索引不存在的元素,Rust 會停止執(zhí)行并拒絕繼續(xù)。嘗試運行上面的程序會出現(xiàn)如下:
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/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
錯誤指向 main.rs
的第 4 行,這里我們嘗試訪問索引 99。下面的說明(note)行提醒我們可以設(shè)置 RUST_BACKTRACE
環(huán)境變量來得到一個 backtrace。backtrace 是一個執(zhí)行到目前位置所有被調(diào)用的函數(shù)的列表。Rust 的 backtrace 跟其他語言中的一樣:閱讀 backtrace 的關(guān)鍵是從頭開始讀直到發(fā)現(xiàn)你編寫的文件。這就是問題的發(fā)源地。這一行往上是你的代碼所調(diào)用的代碼;往下則是調(diào)用你的代碼的代碼。這些行可能包含核心 Rust 代碼,標準庫代碼或用到的 crate 代碼。讓我們將 RUST_BACKTRACE
環(huán)境變量設(shè)置為任何不是 0 的值來獲取 backtrace 看看。示例 9-2 展示了與你看到類似的輸出:
$ 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/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/std/src/panicking.rs:483
1: core::panicking::panic_fmt
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/panicking.rs:85
2: core::panicking::panic_bounds_check
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/panicking.rs:62
3: <usize as core::slice::index::SliceIndex<[T]>>::index
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/slice/index.rs:255
4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/slice/index.rs:15
5: <alloc::vec::Vec<T> as core::ops::index::Index<I>>::index
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/alloc/src/vec.rs:1982
6: panic::main
at ./src/main.rs:4
7: core::ops::function::FnOnce::call_once
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/ops/function.rs:227
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
示例 9-2:當設(shè)置 RUST_BACKTRACE
環(huán)境變量時 panic!
調(diào)用所生成的 backtrace 信息
這里有大量的輸出!你實際看到的輸出可能因不同的操作系統(tǒng)和 Rust 版本而有所不同。為了獲取帶有這些信息的 backtrace,必須啟用 debug 標識。當不使用 --release
參數(shù)運行 cargo build 或 cargo run 時 debug 標識會默認啟用,就像這里一樣。
示例 9-2 的輸出中,backtrace 的 12 行指向了我們項目中造成問題的行:src/main.rs 的第 4 行。如果你不希望程序 panic,第一個提到我們編寫的代碼行的位置是你應(yīng)該開始調(diào)查的,以便查明是什么值如何在這個地方引起了 panic。在示例 9-1 中,我們故意編寫會 panic 的代碼來演示如何使用 backtrace,修復(fù)這個 panic 的方法就是不要嘗試在一個只包含三個項的 vector 中請求索引是 100 的元素。當將來你的代碼出現(xiàn)了 panic,你需要搞清楚在這特定的場景下代碼中執(zhí)行了什么操作和什么值導(dǎo)致了 panic,以及應(yīng)當如何處理才能避免這個問題。
本章后面的小節(jié) “panic! 還是不 panic!” 會再次回到 panic!
并講解何時應(yīng)該、何時不應(yīng)該使用 panic!
來處理錯誤情況。接下來,我們來看看如何使用 Result
來從錯誤中恢復(fù)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: