平常我們寫網頁的時候,也會有錯誤的 exception 處理,
避免錯誤發生的時候出現非預期的畫面
Rust 中也有 Error Handling 的機制
在開發時,難免都會有錯誤的程式碼,
有錯誤處理機制是為了預防我們將程式碼部署到正式環境後,出現錯誤的狀況,
像是預先處理、預防的概念,
而這樣的機制對於開發者算是好事,
寧可在開發時把錯誤處理,也不希望到正式環境錯誤一大堆。
這邊我們要分成 unrecoverable 以及 recoverable 的兩種,我們先來介紹第一種
指的是當錯誤發生時,後面的程式碼都不會編譯,就中斷在這邊,會以 unrecoverable 來處理錯誤時,通常都是程式碼裡面有 bug,希望開發者能將這個問題處理好,否則就無法繼續編譯或執行
unrecoverable 主要是使用 panic!
這個方法來處理
panic!
是一種 macro ,通常會在程式碼錯誤的時候呼叫 panic!
,讓程式碼無法繼續編譯或執行,這其實有點像是安全機制,告訴你病毒在哪裡,趕快去消滅它!
fn main() {
println!("Hey, Error will happen!");
// Explicitly exit the program with an unrecoverable error
panic!("Crash");
println!("Can you see me?");
}
當我們執行時,就會出現 thread 'main' panicked at 'Crash', src/main.rs:5:5
我們來解析一下他的意思
thread
的意思是,錯誤是發生在哪裡,主執行序是在 main() 執行,所以就是 thread 'main'
panicked
指的是錯誤發生在哪裡,並且指出在程式碼的哪一行
> cargo run
Hey, Error will happen!
thread 'main' panicked at 'Crash', src/main.rs:5:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
讓我們再來看看一個例子,我們要印出陣列 index 2 的值
(可是其實沒有 index 2)
fn main() {
let a = [1, 2];
println!("這個陣列最後一個值是 {:?}", a[2]);
println!("Can you see me?");
}
當我們執行時,他會直接噴錯,連編譯都無法
> cargo run
Compiling hello_world v0.1.0 (/Users/zhangjianing/Desktop/Side Project/rust_practice/hello_world)
error: this operation will panic at runtime
--> src/main.rs:4:33
|
4 | println!("這個陣列最後一個值是 {:?}", a[2]);
| ^^^^ index out of bounds: the length is 2 but the index is 2
|
= note: `#[deny(unconditional_panic)]` on by default
error: could not compile `hello_world` (bin "hello_world") due to previous error
在 C 語言中,當我們要取出的 index 值是陣列沒有的,有可能會取到整個記憶體中的 index ,即便這個值不是屬於該陣列的,如果有人蓄意要攻擊,就可能會導致安全性問題
而 Rust 為了避免這樣的狀況發生,就會讓 panic!
發出錯誤警告,並且避免程式繼續執行下去
這邊就比較少是 bug 問題,通常都是操作上的問題,像是文件遺失、無權限、型態解析錯誤等等
在 Rust 中會用 Result
以及 Option
來處理 recoverable
Result<T, E>
來表示結果是成功還是失敗,需要兩個參數,一個是 T
,一個是 E
,其實這兩個參數是
T
代表著成功後回傳的變數E
則是失敗回傳的錯誤訊息
我們之後會再討論 T
跟 E
來看看應用的例子,我今天要去解析字串成數字,就可以用 Result 去看是否可以解析成功,以下是解析成功的例子
fn main() {
let number: Result<i32, _> = "123".parse();
match number {
Ok(num) => println!("解析成功:{}", num),
Err(error) => println!("無法解析:{}", error),
}
}
編譯並且執行後,會執行 Ok 這條
> cargo run
Compiling hello_world v0.1.0 (/Users/zhangjianing/Desktop/Side Project/rust_practice/hello_world)
Finished dev [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/hello_world`
解析成功:123
如果是失敗的例子呢?他就會走 Err 這條
fn main() {
let number: Result<i32, _> = "一二三".parse();
match number {
Ok(num) => println!("解析成功:{}", num),
Err(error) => println!("無法解析:{}", error),
}
}
編譯並且執行後
> cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
Running `target/debug/hello_world`
無法解析:invalid digit found in string
剛剛有看到一個 match 的東西,我們可以把 match 看作是一個語法糖衣,如果沒有 match ,我們就需要用 panic!
來呈現錯誤訊息
Option<T>
適用於此變數或者結果是否存在,會有 None 以及 Some 這兩個結果
這邊的 T 跟 Result 不太一樣,可能會是 Some 或者是 None,不過他們都是參數
Some(c) 表示存在,並且回傳 c (不一定要叫 c ,想叫什麼都可)變數
None 表示不存在
讓我們來看看例子吧
fn main() {
let a = [1, 2, 3];
let b = a.get(2);
let _c = match b {
None => println!("沒有這個值"),
Some(c) => println!("結果為:{}", c),
};
}
編譯並執行後,會得到 c
> cargo run
Compiling hello_world v0.1.0 (/Users/zhangjianing/Desktop/Side Project/rust_practice/hello_world)
Finished dev [unoptimized + debuginfo] target(s) in 0.15s
Running `target/debug/hello_world`
結果為:3
那如果值不存在呢?
fn main() {
let a = [1, 2, 3];
let b = a.get(3);
let _c = match b {
None => println!("沒有這個值"),
Some(c) => println!("{}", c),
};
}
編譯並且執行後,就會走 None 這條
> cargo run
Compiling hello_world v0.1.0 (/Users/zhangjianing/Desktop/Side Project/rust_practice/hello_world)
Finished dev [unoptimized + debuginfo] target(s) in 0.09s
Running `target/debug/hello_world`
沒有這個值
Result
與 Option
在應用上其實不太一樣,
Result
著重在結果是成功還是失敗Option
主要應用在這個東西是否存在
不過他們兩個都會與 match
來做搭配應用