@Kais(VagrantPi)
Rust
, 簡報
, Rust 讀書會
Cargo.toml 的 [profile] 部分增加 panic = ‘abort’
fn main() { panic!("crash and burn"); }
$ cargo run
Compiling rust v0.1.0 (file:///Users/kais.lin/rust)
Finished dev [unoptimized + debuginfo] target(s) in 3.42 secs
Running `target/debug/rust`
thread 'main' panicked at 'crash and burn', main.rs:2:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.
fn main() { let v = vec![1, 2, 3]; v[99]; }
$ cargo run
Compiling rust v0.1.0 (file:///Users/kais.lin/rust)
Finished dev [unoptimized + debuginfo] target(s) in 0.34 secs
Running `target/debug/rust`
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', /Users/travis/build/rust-lang/rust/src/libcore/slice/mod.rs:865:10
note: Run with `RUST_BACKTRACE=1` for a backtrace.
$ RUST_BACKTRACE=1 cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/rust`
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', /Users/travis/build/rust-lang/rust/src/libcore/slice/mod.rs:865:10
stack backtrace:
0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
1: std::sys_common::backtrace::print
at libstd/sys_common/backtrace.rs:71
at libstd/sys_common/backtrace.rs:59
2: std::panicking::default_hook::{{closure}}
at libstd/panicking.rs:207
3: std::panicking::default_hook
at libstd/panicking.rs:223
4: std::panicking::begin_panic
at libstd/panicking.rs:402
5: std::panicking::try::do_call
at libstd/panicking.rs:349
6: std::panicking::try::do_call
at libstd/panicking.rs:325
7: core::ptr::drop_in_place
at libcore/panicking.rs:72
8: core::ptr::drop_in_place
at libcore/panicking.rs:58
9: <usize as core::slice::SliceIndex<[T]>>::index
at /Users/travis/build/rust-lang/rust/src/libcore/slice/mod.rs:865
10: core::slice::<impl core::ops::index::Index<I> for [T]>::index
at /Users/travis/build/rust-lang/rust/src/libcore/slice/mod.rs:767
11: <alloc::vec::Vec<T> as core::ops::index::Index<I>>::index
at /Users/travis/build/rust-lang/rust/src/liballoc/vec.rs:1648
12: rust::main
at ./main.rs:4
13: std::rt::lang_start::{{closure}}
at /Users/travis/build/rust-lang/rust/src/libstd/rt.rs:74
14: std::panicking::try::do_call
at libstd/rt.rs:59
at libstd/panicking.rs:306
15: panic_unwind::dwarf::eh::read_encoded_pointer
at libpanic_unwind/lib.rs:102
16: <std::io::Write::write_fmt::Adaptor<'a, T> as core::fmt::Write>::write_str
at libstd/panicking.rs:285
at libstd/panic.rs:361
at libstd/rt.rs:58
17: std::rt::lang_start
at /Users/travis/build/rust-lang/rust/src/libstd/rt.rs:74
18: rust::main
12: rust::main
at ./main.rs:4
使用 Result
類型來處理潛在錯誤
enum Result<T, E> {
Ok(T),
Err(E),
}
如何知道某某函數會回傳 Rusult
呢?除文件之外,書中也提到了另一個方法
use std::fs::File; fn main() { let f = File::open("hello.txt"); }
error[E0308]: mismatched types
--> src/main.rs:4:18
|
4 | let f: u32 = File::open("hello.txt");
| ^^^^^^^^^^^^^^^^^^^^^^^ expected u32, found enum
`std::result::Result`
|
= note: expected type `u32`
found type `std::result::Result<std::fs::File, std::io::Error>`
use std::fs::File; fn main() { let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => { panic!("There was a problem opening the file: {:?}", error) }, }; }
use std::fs::File; use std::io::ErrorKind; fn main() { let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(ref error) if error.kind() == ErrorKind::NotFound => { match File::create("hello.txt") { Ok(fc) => fc, Err(e) => { panic!( "Tried to create file but there was a problem: {:?}", e ) }, } }, Err(error) => { panic!( "There was a problem opening the file: {:?}", error ) }, }; }
if error.kind() == ErrorKind::NotFound 被稱為 match guard
,用來更完善 match 的而外條件判斷
use std::fs::File; fn main() { let f = File::open("hello.txt").unwrap(); }
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error {
repr: Os { code: 2, message: "No such file or directory" } }',
src/libcore/result.rs:906:4
use std::fs::File; fn main() { let f = File::open("hello.txt").expect("Failed to open hello.txt"); }
thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
2, message: "No such file or directory" } }', src/libcore/result.rs:906:4
簡單說就是 function 會回傳 error,這樣可以讓呼叫他的地方能夠判斷要不要做錯誤處裡
use std::io; use std::io::Read; use std::fs::File; fn read_username_from_file() -> Result<String, io::Error> { let f = File::open("hello.txt"); let mut f = match f { Ok(file) => file, Err(e) => return Err(e), }; let mut s = String::new(); match f.read_to_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), } }
use std::io; use std::io::Read; use std::fs::File; 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) }
原文理解不能,以下是我個人理解,請大大們勘誤
?
會將錯誤值丟給 from 函數(定義於 From trait 中),可以將 Result 回傳的錯誤型別,轉換成該 fn 回傳的錯誤型別
use std::io::{self, Read}; use std::num; enum CliError { IoError(io::Error), ParseError(num::ParseIntError), } impl From<io::Error> for CliError { fn from(error: io::Error) -> Self { CliError::IoError(error) } } impl From<num::ParseIntError> for CliError { fn from(error: num::ParseIntError) -> Self { CliError::ParseError(error) } } fn open_and_parse_file(file_name: &str) -> Result<i32, CliError> { let mut file = std::fs::File::open("test")?; let mut contents = String::new(); file.read_to_string(&mut contents)?; let num: i32 = contents.trim().parse()?; Ok(num) }
use std::fs::File; fn main() { let f = File::open("hello.txt")?; }
error[E0277]: the trait bound `(): std::ops::Try` is not satisfied
--> src/main.rs:4:13
|
4 | let f = File::open("hello.txt")?;
| ------------------------
| |
| the `?` operator can only be used in a function that returns
`Result` (or another type that implements `std::ops::Try`)
| in this macro invocation
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
有時候只是要快速的驗證或者是 demo,可以先 unwrap 之類的方法,未來再來實現這部廢的錯誤處理
use std::net::IpAddr; let home: IpAddr = "127.0.0.1".parse().unwrap();
即便是寫死的,而且確定是可分析的 ip,編譯器仍會覺得還是可能會出現 Err 因此要我們處理 Result,這邊種情況下使用 unwrap 是合適的
預期外的傳入值,可能導致存取非當前資料的記憶體空間,而出現安全性問題
不過透過 Rust 的 type system 就會為你做很多檢查了(EX: u32 接到的值不會有負的)
v0.0.1
loop { // --snip-- let guess: i32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, }; if guess < 1 || guess > 100 { println!("The secret number will be between 1 and 100."); continue; } match guess.cmp(&secret_number) { // --snip-- }
不過這樣但這樣每次都要進一次判斷式,如果很多的函式都需要這樣的檢查,這樣寫就恨很冗餘(也許也會小小的引響到效能)
pub struct Guess { value: u32, } impl Guess { pub fn new(value: u32) -> Guess { if value < 1 || value > 100 { panic!("Guess value must be between 1 and 100, got {}.", value); } Guess { value } } pub fn value(&self) -> u32 { self.value } }
v0.0.2
extern crate rand; use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Guess the number!"); let secret_number = rand::thread_rng().gen_range(1, 101); loop { println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect("Failed to read line"); let guess: u32 = match guess.trim().parse() { - Ok(num) => num, + Ok(num) => Guess::new(num).value(), Err(_) => continue, }; println!("You guessed: {}", guess); match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => { println!("You win!"); break; } } } } + pub struct Guess { + value: u32, + } + + impl Guess { + pub fn new(value: u32) -> Guess { + if value < 1 || value > 100 { + panic!("Guess value must be between 1 and +100, got {}.", value); + } + + Guess { + value + } + } + + pub fn value(&self) -> u32 { + self.value + } + }