@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
+ }
+ }