Справяне с грешки
27 октомври 2022
Административни неща
- Първо домашно приключи
- Второ предизвикателство е отворено, ако ви се занимава за бонус точки: https://fmi.rust-lang.bg/challenges/2
Преговор
Преговор
- Generics (мономорфизация)
Преговор
- Generics (мономорфизация)
- Traits (като интерфейси, необходими за практическа употреба на generics)
Преговор
- Generics (мономорфизация)
- Traits (като интерфейси, необходими за практическа употреба на generics)
- Асоциирани типове в trait-ове
Преговор
- Generics (мономорфизация)
- Traits (като интерфейси, необходими за практическа употреба на generics)
- Асоциирани типове в trait-ове
const fn
-ове за ентусиасти (чакайте да стигнем до procedural macros)
Конвертиране
Конвертиране
1
2
3
4
5
6
7
struct Celsius(f64);
struct Fahrenheit(f64);
struct Kelvin(f64);
fn room_temperature() -> Fahrenheit {
Fahrenheit(68.0)
}
Конвертиране
1
2
3
4
5
6
7
8
9
10
11
struct Celsius(f64);
struct Fahrenheit(f64);
struct Kelvin(f64);
fn room_temperature() -> Fahrenheit {
Fahrenheit(68.0)
}
fn energy_to_heat_water(from: Kelvin, to: Kelvin, mass: f64) -> f64 {
// whatever
}
Конвертиране
From
1
2
3
4
5
6
7
8
9
10
11
impl From<Celsius> for Kelvin {
fn from(t: Celsius) -> Kelvin { Kelvin(t.0 + 273.15) }
}
impl From<Fahrenheit> for Celsius {
fn from(t: Fahrenheit) -> Celsius { Celsius((t.0 - 32) / 1.8) }
}
impl From<Fahrenheit> for Kelvin {
fn from(t: Fahrenheit) -> Kelvin { Kelvin::from(Celsius::from(t)) }
}
Конвертиране
From
Сега вече можем да си сварим яйца
1
2
3
4
5
6
let e = energy_to_heat_water(
Kelvin::from(room_temperature()),
Kelvin::from(Celsius(100.0)),
1.0
);
println!("Heating water will cost {}J", e);
Конвертиране
From
1
2
3
pub trait From<T> {
fn from(T) -> Self;
}
Конвертиране
From
1
2
3
pub trait From<T> {
fn from(T) -> Self;
}
From<T> for U
конвертира отT
доU
From<T> for T
е имплементирано автоматично (в стандартната библиотека)From<Source> for Target
-- може би по-ясни имена- Конвертирането не може да се провали
Конвертиране
Into
U::from(t)
е дълго за писане- Затова съществува "реципрочен" метод
1
2
3
pub trait Into<T> {
fn into(self) -> T;
}
Конвертиране
Into
U::from(t)
е дълго за писане- Затова съществува "реципрочен" метод
1
2
3
pub trait Into<T> {
fn into(self) -> T;
}
Into<U> for T
конвертира отT
доU
Into<Target> for Source
, иначе казано
Конвертиране
Into
U::from(t)
е дълго за писане- Затова съществува "реципрочен" метод
1
2
3
pub trait Into<T> {
fn into(self) -> T;
}
Into<U> for T
конвертира отT
доU
Into<Target> for Source
, иначе казаноInto<T> for T
е имплементирано автоматично
Конвертиране
Into
U::from(t)
е дълго за писане- Затова съществува "реципрочен" метод
1
2
3
pub trait Into<T> {
fn into(self) -> T;
}
Into<U> for T
конвертира отT
доU
Into<Target> for Source
, иначе казаноInto<T> for T
е имплементирано автоматичноInto<U> for T
се имплементира автоматично като имплементирамеFrom<T> for U
(в стандартната библиотека)From<Source> for Target
=>Into<Target> for Source
- Практиката е ръчно да се имплементира
From
Конвертиране
Into
1
impl From<Celsius> for Kelvin {
Е еквивалентно на:
1
impl Into<Kelvin> for Celsius {
Конвертиране
Into
1
2
3
4
5
6
7
8
9
10
11
12
13
// използвайки From
let e = energy_to_heat_water(
Kelvin::from(room_temperature()),
Kelvin::from(Celsius(100.0)),
1.0
);
// използвайки Into
let e = energy_to_heat_water(
room_temperature().into(),
Celsius(100.0).into(),
1.0
);
Конвертиране
Generics
Понякога библиотечни функции не взимат T
, а нещо което може да се конвертира до T
1
2
3
4
5
6
7
8
9
10
11
fn energy_to_heat_water<T1, T2>(from: T1, to: T2, mass: f64) -> f64
where
T1: Into<Kelvin>,
T2: Into<Kelvin>
{
let from = from.into();
let to = to.into();
// whatever
}
let e = energy_to_heat_water(room_temperature(), Celsius(100.0), 1.0);
Конвертиране
Into
- Супер удобно
Конвертиране
Into
- Супер удобно
- Но, може да затрудни малко разбирането на кода ("твърде много магия")
Конвертиране
Into
- Супер удобно
- Но, може да затрудни малко разбирането на кода ("твърде много магия")
- Използвайте го! Просто не прекалявайте :)
Конвертиране
Into
- Супер удобно
- Но, може да затрудни малко разбирането на кода ("твърде много магия")
- Използвайте го! Просто не прекалявайте :)
- (Или прекалете един-два пъти, за да го усетите :))
Конвертиране
Into
- Супер удобно
- Но, може да затрудни малко разбирането на кода ("твърде много магия")
- Използвайте го! Просто не прекалявайте :)
- (Или прекалете един-два пъти, за да го усетите :))
- Ще го срещате тук и там
String parsing
String parsing
- Ами ако искаме да конструираме стойност от низ? (от JSON низ, от XML)
String parsing
- Ами ако искаме да конструираме стойност от низ? (от JSON низ, от XML)
- Не можем да използваме
From
, защото не сме сигурни че създаването ще е успешно - Има специален trait за това
String parsing
FromStr
1
2
3
4
5
6
7
8
9
10
trait FromStr {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
enum Result<T, E> {
Ok(T),
Err(E),
}
- Конвертиране от низ до наш си тип
- Връща
Result
който показва дали конвертирането е успешно
String parsing
FromStr
1
2
3
4
5
6
7
use std::str::FromStr;
let x = i32::from_str("-13");
let y = u8::from_str("323");
let z = f32::from_str("5e-3");
println!("{:?}\n{:?}\n{:?}", x, y, z);
Ok(-13) Err(ParseIntError { kind: PosOverflow }) Ok(0.005)
fn main() { use std::str::FromStr; let x = i32::from_str("-13"); let y = u8::from_str("323"); let z = f32::from_str("5e-3"); println!("{:?}\n{:?}\n{:?}", x, y, z); }
String parsing
parse
И тук има реципрочен метод
String parsing
parse
И тук има реципрочен метод
1
2
3
4
5
6
7
8
trait FromStr {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
impl str {
fn parse<F: FromStr>(&self) -> Result<F, <F as FromStr>::Err> { ... }
}
- Типа
<F as FromStr>::Err
е "типаErr
, който е дефиниран заFromStr
имплементацията наF
"
String parsing
parse
И тук има реципрочен метод
1
2
3
4
5
6
7
8
trait FromStr {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
impl str {
fn parse<F: FromStr>(&self) -> Result<F, <F as FromStr>::Err> { ... }
}
- Типа
<F as FromStr>::Err
е "типаErr
, който е дефиниран заFromStr
имплементацията наF
" - Generic параметъра F трябва да имплементира
FromStr
(подобно наInto
заFrom
)
String parsing
parse
И тук има реципрочен метод
1
2
3
4
5
6
7
8
trait FromStr {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
impl str {
fn parse<F: FromStr>(&self) -> Result<F, <F as FromStr>::Err> { ... }
}
- Типа
<F as FromStr>::Err
е "типаErr
, който е дефиниран заFromStr
имплементацията наF
" - Generic параметъра F трябва да имплементира
FromStr
(подобно наInto
заFrom
) - Метода
parse
е имплементиран върхуstr
String parsing
parse
И тук има реципрочен метод
1
2
3
4
5
6
7
8
trait FromStr {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
impl str {
fn parse<F: FromStr>(&self) -> Result<F, <F as FromStr>::Err> { ... }
}
- Типа
<F as FromStr>::Err
е "типаErr
, който е дефиниран заFromStr
имплементацията наF
" - Generic параметъра F трябва да имплементира
FromStr
(подобно наInto
заFrom
) - Метода
parse
е имплементиран върхуstr
- Метода
parse
е generic по return стойността си
String parsing
parse
1
2
3
4
5
let x = "-13".parse::<i32>();
let y = "323".parse::<u8>();
let z = "5e-3".parse::<f32>();
println!("{:?}\n{:?}\n{:?}", x, y, z);
Ok(-13) Err(ParseIntError { kind: PosOverflow }) Ok(0.005)
fn main() { let x = "-13".parse::(); let y = "323".parse:: (); let z = "5e-3".parse:: (); println!("{:?}\n{:?}\n{:?}", x, y, z); }
String parsing
parse
1
2
3
4
5
let x: Result<i32, <i32 as FromStr>::Err> = "-13".parse();
let y: Result<u8, <u8 as FromStr>::Err> = "323".parse();
let z: Result<f32, <f32 as FromStr>::Err> = "5e-3".parse();
println!("{:?}\n{:?}\n{:?}", x, y, z);
Ok(-13) Err(ParseIntError { kind: PosOverflow }) Ok(0.005)
use std::str::FromStr; fn main() { let x: Result::Err> = "-13".parse(); let y: Result ::Err> = "323".parse(); let z: Result ::Err> = "5e-3".parse(); println!("{:?}\n{:?}\n{:?}", x, y, z); }
String parsing
parse
1
2
3
4
5
let x: Result<i32, _> = "-13".parse();
let y: Result<u8, _> = "323".parse();
let z: Result<f32, _> = "5e-3".parse();
println!("{:?}\n{:?}\n{:?}", x, y, z);
Ok(-13) Err(ParseIntError { kind: PosOverflow }) Ok(0.005)
fn main() { let x: Result= "-13".parse(); let y: Result = "323".parse(); let z: Result = "5e-3".parse(); println!("{:?}\n{:?}\n{:?}", x, y, z); }
String parsing
parse
1
2
3
4
5
let x: i32 = "-13".parse().unwrap();
// let y: u8 = "323".parse().unwrap();
let z: f32 = "5e-3".parse().unwrap();
println!("{:?}\n???\n{:?}", x, z);
-13 ??? 0.005
fn main() { let x: i32 = "-13".parse().unwrap(); // let y: u8 = "323".parse().unwrap(); let z: f32 = "5e-3".parse().unwrap(); println!("{:?}\n???\n{:?}", x, z); }
String parsing
parse
1
2
3
4
5
6
7
use std::str::FromStr;
#[derive(Debug)]
struct Student {
name: String,
faculty_number: String,
}
String parsing
parse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
impl FromStr for Student {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.split(", ").collect::<Vec<_>>().as_slice() {
[name, faculty_number] => {
let name = name.to_string();
let faculty_number = faculty_number.to_string();
Ok(Self { name, faculty_number })
},
_ => Err(String::from("🤷🤷🤷")),
}
}
}
use std::str::FromStr; #[derive(Debug)] struct Student { name: String, faculty_number: String, } fn main() { impl FromStr for Student { type Err = String; fn from_str(s: &str) -> Result{ match s.split(", ").collect:: >().as_slice() { [name, faculty_number] => { let name = name.to_string(); let faculty_number = faculty_number.to_string(); Ok(Self { name, faculty_number }) }, _ => Err(String::from("🤷🤷🤷")), } } } }
String parsing
parse
1
2
3
4
5
6
7
fn main() {
let s1: Result<Student, _> = "Данчо Е. Студент, 12345".parse();
let s2: Result<Student, _> = "Гинка Билоба, 77777".parse();
let s3: Result<Student, _> = "Бял Мерцедес".parse();
println!("{:?}\n{:?}\n{:?}", s1, s2, s3);
}
Ok(Student { name: "Данчо Е. Студент", faculty_number: "12345" }) Ok(Student { name: "Гинка Билоба", faculty_number: "77777" }) Err("🤷🤷🤷")
use std::str::FromStr; #[derive(Debug)] struct Student { name: String, faculty_number: String, } impl FromStr for Student { type Err = String; fn from_str(s: &str) -> Result{ match s.split(", ").collect:: >().as_slice() { [name, faculty_number] => { let name = name.to_string(); let faculty_number = faculty_number.to_string(); Ok(Self { name, faculty_number }) }, _ => Err(String::from("🤷🤷🤷")), } } } fn main() { let s1: Result = "Данчо Е. Студент, 12345".parse(); let s2: Result = "Гинка Билоба, 77777".parse(); let s3: Result = "Бял Мерцедес".parse(); println!("{:?}\n{:?}\n{:?}", s1, s2, s3); }
String parsing
parse
1
2
3
4
5
6
7
fn main() {
let s1 = "Данчо Е. Студент, 12345".parse::<Student>();
let s2 = "Гинка Билоба, 77777".parse::<Student>();
let s3 = "Бял Мерцедес".parse::<Student>();
println!("{:?}\n{:?}\n{:?}", s1, s2, s3);
}
Ok(Student { name: "Данчо Е. Студент", faculty_number: "12345" }) Ok(Student { name: "Гинка Билоба", faculty_number: "77777" }) Err("🤷🤷🤷")
use std::str::FromStr; #[derive(Debug)] struct Student { name: String, faculty_number: String, } impl FromStr for Student { type Err = String; fn from_str(s: &str) -> Result{ match s.split(", ").collect:: >().as_slice() { [name, faculty_number] => { let name = name.to_string(); let faculty_number = faculty_number.to_string(); Ok(Self { name, faculty_number }) }, _ => Err(String::from("🤷🤷🤷")), } } } fn main() { let s1 = "Данчо Е. Студент, 12345".parse:: (); let s2 = "Гинка Билоба, 77777".parse:: (); let s3 = "Бял Мерцедес".parse:: (); println!("{:?}\n{:?}\n{:?}", s1, s2, s3); }
Error handling
Error handling
1
2
3
4
5
6
7
8
9
10
11
use std::fs::File;
use std::io::Read;
fn main() {
let mut file = File::open("deep_quotes.txt");
let mut contents = String::new();
file.read_to_string(&mut contents);
println!("{}", contents);
}
Error handling
1
2
3
4
5
6
7
8
9
10
11
use std::fs::File;
use std::io::Read;
fn main() {
let mut file = File::open("deep_quotes.txt");
let mut contents = String::new();
file.read_to_string(&mut contents);
println!("{}", contents);
}
error[E0599]: no method named `read_to_string` found for enum `Result` in the current scope --> src/bin/main_c656429131e242e34ce39a654c1457567c9b5847.rs:8:10 | 8 | file.read_to_string(&mut contents); | ^^^^^^^^^^^^^^ method not found in `Result<File, std::io::Error>` | note: the method `read_to_string` exists on the type `File` --> /home/andrew/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/io/mod.rs:745:5 | 745 | fn read_to_string(&mut self, buf: &mut String) -> Result<usize> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `Result::expect` to unwrap the `File` value, panicking if the value is a `Result::Err` | 8 | file.expect("REASON").read_to_string(&mut contents); | +++++++++++++++++ warning: unused import: `std::io::Read` --> src/bin/main_c656429131e242e34ce39a654c1457567c9b5847.rs:2:5 | 2 | use std::io::Read; | ^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default For more information about this error, try `rustc --explain E0599`. error: could not compile `rust` due to previous error; 1 warning emitted
use std::fs::File; use std::io::Read; fn main() { let mut file = File::open("deep_quotes.txt"); let mut contents = String::new(); file.read_to_string(&mut contents); println!("{}", contents); }
Error handling
1
2
3
4
5
6
7
8
9
enum Result<T, E> {
Ok(T),
Err(E),
}
File::open("excellent_file.txt")
// => Ok(std::fs::File)
File::open("broken_file.txt")
// => Err(std::io::Error)
Error handling
1
2
3
4
5
6
7
8
9
10
11
12
13
use std::fs::File;
use std::io::Read;
fn main() {
let mut file = match File::open("deep_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
println!("{}", contents);
}
Failure is just success rounded down, my friend!
use std::fs::File; use std::io::Read; fn main() { let file_contents = "Failure is just success rounded down, my friend!\n"; std::fs::write("deep_quotes.txt", file_contents.as_bytes()).unwrap(); let mut file = match File::open("deep_quotes.txt") { Ok(f) => f, Err(e) => panic!("😞 {}", e), }; let mut contents = String::new(); file.read_to_string(&mut contents).unwrap(); println!("{}", contents); }
Error handling
1
2
3
4
5
6
7
8
9
10
11
12
13
use std::fs::File;
use std::io::Read;
fn main() {
let mut file = match File::open("shallow_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
println!("{}", contents);
}
thread 'main' panicked at '😞 No such file or directory (os error 2)', src/bin/main_b45a8bd9ca61aae4e55dda1ed96518a5ab7fe076.rs:7:19 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
use std::fs::File; use std::io::Read; fn main() { let mut file = match File::open("shallow_quotes.txt") { Ok(f) => f, Err(e) => panic!("😞 {}", e), }; let mut contents = String::new(); file.read_to_string(&mut contents).unwrap(); println!("{}", contents); }
Error handling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn main() {
let mut deep = match File::open("deep_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut wide = match File::open("wide_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut contents = String::new();
deep.read_to_string(&mut contents).unwrap();
wide.read_to_string(&mut contents).unwrap();
println!("{}", contents);
}
Failure is just success rounded down, my friend! F a i l u r e i s j u s t s u c c e s s r o u n d e d d o w n , m y f r i e n d !
use std::fs::File; use std::io::Read; fn main() { let file_contents = "Failure is just success rounded down, my friend!\n"; std::fs::write("deep_quotes.txt", file_contents.as_bytes()).unwrap(); let file_contents = "F a i l u r e i s j u s t s u c c e s s r o u n d e d d o w n , m y f r i e n d !\n"; std::fs::write("wide_quotes.txt", file_contents.as_bytes()).unwrap(); let mut deep = match File::open("deep_quotes.txt") { Ok(f) => f, Err(e) => panic!("😞 {}", e), }; let mut wide = match File::open("wide_quotes.txt") { Ok(f) => f, Err(e) => panic!("😞 {}", e), }; let mut contents = String::new(); deep.read_to_string(&mut contents).unwrap(); wide.read_to_string(&mut contents).unwrap(); println!("{}", contents); }
Error handling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
all_your_quotes_are_belong_to_us();
}
fn all_your_quotes_are_belong_to_us() {
let mut deep = match File::open("deep_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut wide = match File::open("wide_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut contents = String::new();
deep.read_to_string(&mut contents).unwrap();
wide.read_to_string(&mut contents).unwrap();
println!("{}", contents);
}
Failure is just success rounded down, my friend! F a i l u r e i s j u s t s u c c e s s r o u n d e d d o w n , m y f r i e n d !
use std::fs::File; use std::io::Read; fn main() { let file_contents = "Failure is just success rounded down, my friend!\n"; std::fs::write("deep_quotes.txt", file_contents.as_bytes()).unwrap(); let file_contents = "F a i l u r e i s j u s t s u c c e s s r o u n d e d d o w n , m y f r i e n d !\n"; std::fs::write("wide_quotes.txt", file_contents.as_bytes()).unwrap(); all_your_quotes_are_belong_to_us(); } fn all_your_quotes_are_belong_to_us() { let mut deep = match File::open("deep_quotes.txt") { Ok(f) => f, Err(e) => panic!("😞 {}", e), }; let mut wide = match File::open("wide_quotes.txt") { Ok(f) => f, Err(e) => panic!("😞 {}", e), }; let mut contents = String::new(); deep.read_to_string(&mut contents).unwrap(); wide.read_to_string(&mut contents).unwrap(); println!("{}", contents); }
Error handling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn main() {
match all_your_quotes_are_belong_to_us() {
Ok(contents) => println!("{}", contents),
Err(e) => panic!("😞 {}", e),
}
}
fn all_your_quotes_are_belong_to_us() -> Result<String, io::Error> {
let mut deep = match File::open("deep_quotes.txt") {
Ok(f) => f,
Err(e) => return Err(e),
};
let mut wide = match File::open("wide_quotes.txt") {
Ok(f) => f,
Err(e) => return Err(e),
};
let mut contents = String::new();
deep.read_to_string(&mut contents).unwrap();
wide.read_to_string(&mut contents).unwrap();
Ok(contents)
}
Failure is just success rounded down, my friend! F a i l u r e i s j u s t s u c c e s s r o u n d e d d o w n , m y f r i e n d !
use std::fs::File; use std::io::{self, Read}; fn main() { let file_contents = "Failure is just success rounded down, my friend!\n"; std::fs::write("deep_quotes.txt", file_contents.as_bytes()).unwrap(); let file_contents = "F a i l u r e i s j u s t s u c c e s s r o u n d e d d o w n , m y f r i e n d !\n"; std::fs::write("wide_quotes.txt", file_contents.as_bytes()).unwrap(); match all_your_quotes_are_belong_to_us() { Ok(contents) => println!("{}", contents), Err(e) => panic!("😞 {}", e), } } fn all_your_quotes_are_belong_to_us() -> Result{ let mut deep = match File::open("deep_quotes.txt") { Ok(f) => f, Err(e) => return Err(e), }; let mut wide = match File::open("wide_quotes.txt") { Ok(f) => f, Err(e) => return Err(e), }; let mut contents = String::new(); deep.read_to_string(&mut contents).unwrap(); wide.read_to_string(&mut contents).unwrap(); Ok(contents) }
Error handling
A wild macro appears
1
2
3
4
5
6
7
8
macro_rules! _try {
($expr:expr) => {
match $expr {
Ok(result) => result,
Err(e) => return Err(e),
}
}
}
Error handling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
match all_your_quotes_are_belong_to_us() {
Ok(contents) => println!("{}", contents),
Err(e) => panic!("😞 {}", e),
}
}
fn all_your_quotes_are_belong_to_us() -> Result<String, io::Error> {
let mut deep = _try!(File::open("deep_quotes.txt"));
let mut wide = _try!(File::open("wide_quotes.txt"));
let mut contents = String::new();
deep.read_to_string(&mut contents).unwrap();
wide.read_to_string(&mut contents).unwrap();
Ok(contents)
}
Failure is just success rounded down, my friend! F a i l u r e i s j u s t s u c c e s s r o u n d e d d o w n , m y f r i e n d !
macro_rules! _try { ($expr:expr) => { match $expr { Ok(result) => result, Err(e) => return Err(e), } } } use std::fs::File; use std::io::{self, Read}; fn main() { let file_contents = "Failure is just success rounded down, my friend!\n"; std::fs::write("deep_quotes.txt", file_contents.as_bytes()).unwrap(); let file_contents = "F a i l u r e i s j u s t s u c c e s s r o u n d e d d o w n , m y f r i e n d !\n"; std::fs::write("wide_quotes.txt", file_contents.as_bytes()).unwrap(); match all_your_quotes_are_belong_to_us() { Ok(contents) => println!("{}", contents), Err(e) => panic!("😞 {}", e), } } fn all_your_quotes_are_belong_to_us() -> Result{ let mut deep = _try!(File::open("deep_quotes.txt")); let mut wide = _try!(File::open("wide_quotes.txt")); let mut contents = String::new(); deep.read_to_string(&mut contents).unwrap(); wide.read_to_string(&mut contents).unwrap(); Ok(contents) }
Error handling
А без онзи unwrap
?
1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut file = match File::open("deep_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut contents = String::new();
file.read_to_string(&mut contents); //.unwrap()
println!("{}", contents);
}
Error handling
А без онзи unwrap
?
1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut file = match File::open("deep_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut contents = String::new();
file.read_to_string(&mut contents); //.unwrap()
println!("{}", contents);
}
warning: unused `Result` that must be used --> src/bin/main_49027aa658eccb8232022959b2a398cc5125fa37.rs:12:5 | 12 | file.read_to_string(&mut contents); //.unwrap() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_must_use)]` on by default = note: this `Result` may be an `Err` variant, which should be handled
use std::fs::File; use std::io::Read; fn main() { let file_contents = "Failure is just success rounded down, my friend!\n"; std::fs::write("deep_quotes.txt", file_contents.as_bytes()).unwrap(); let mut file = match File::open("deep_quotes.txt") { Ok(f) => f, Err(e) => panic!("😞 {}", e), }; let mut contents = String::new(); file.read_to_string(&mut contents); //.unwrap() println!("{}", contents); }
Error handling
А без онзи unwrap
?
1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut file = match File::open("deep_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut contents = String::new();
let _ = file.read_to_string(&mut contents); // Result<usize, io::Error>
println!("{}", contents);
}
Failure is just success rounded down, my friend!
use std::fs::File; use std::io::Read; fn main() { let file_contents = "Failure is just success rounded down, my friend!\n"; std::fs::write("deep_quotes.txt", file_contents.as_bytes()).unwrap(); let mut file = match File::open("deep_quotes.txt") { Ok(f) => f, Err(e) => panic!("😞 {}", e), }; let mut contents = String::new(); let _ = file.read_to_string(&mut contents); // Resultprintln!("{}", contents); }
Error handling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
match all_your_quotes_are_belong_to_us() {
Ok(contents) => println!("{}", contents),
Err(e) => panic!("😞 {}", e),
}
}
fn all_your_quotes_are_belong_to_us() -> Result<String, io::Error> {
let mut deep = _try!(File::open("deep_quotes.txt"));
let mut wide = _try!(File::open("wide_quotes.txt"));
let mut contents = String::new();
_try!(deep.read_to_string(&mut contents));
_try!(wide.read_to_string(&mut contents));
Ok(contents)
}
Error handling
Въпрос
1
2
3
4
5
// ??
fn main() {
_try!(all_your_quotes_are_belong_to_us());
}
Error handling
Въпрос
1
2
3
fn main() {
_try!(all_your_quotes_are_belong_to_us());
}
error[E0308]: mismatched types --> src/bin/main_8192d65bdeb6fb19a7dd9688c696d5d2c9494da4.rs:5:18 | 5 | Err(e) => return Err(e), | ^^^^^^ expected `()`, found enum `Result` ... 13 | fn main() { | - expected `()` because of default return type 14 | _try!(all_your_quotes_are_belong_to_us()); | ----------------------------------------- in this macro invocation | = note: expected unit type `()` found enum `Result<_, std::io::Error>` = note: this error originates in the macro `_try` (in Nightly builds, run with -Z macro-backtrace for more info) For more information about this error, try `rustc --explain E0308`. error: could not compile `rust` due to previous error
macro_rules! _try { ($expr:expr) => { match $expr { Ok(result) => result, Err(e) => return Err(e), } } } fn all_your_quotes_are_belong_to_us() -> Result<(), std::io::Error> { println!("Bla-bla failure success"); Ok(()) } fn main() { _try!(all_your_quotes_are_belong_to_us()); }
Error handling
Въпрос
1
2
3
4
5
6
7
use std::io;
fn main() -> Result<(), io::Error> {
_try!(all_your_quotes_are_belong_to_us());
println!("This is okay");
Ok(())
}
Bla-bla failure success This is okay
macro_rules! _try { ($expr:expr) => { match $expr { Ok(result) => result, Err(e) => return Err(e), } } } fn all_your_quotes_are_belong_to_us() -> Result<(), std::io::Error> { println!("Bla-bla failure success"); Ok(()) } use std::io; fn main() -> Result<(), io::Error> { _try!(all_your_quotes_are_belong_to_us()); println!("This is okay"); Ok(()) }
Error handling
Въпрос
1
2
3
4
5
6
use std::io;
fn main() -> Result<(), io::Error> {
_try!(all_your_quotes_are_belong_to_us());
Err(io::Error::new(io::ErrorKind::Other, "oh no!"))
}
Bla-bla failure success Error: Custom { kind: Other, error: "oh no!" }
macro_rules! _try { ($expr:expr) => { match $expr { Ok(result) => result, Err(e) => return Err(e), } } } fn all_your_quotes_are_belong_to_us() -> Result<(), std::io::Error> { println!("Bla-bla failure success"); Ok(()) } use std::io; fn main() -> Result<(), io::Error> { _try!(all_your_quotes_are_belong_to_us()); Err(io::Error::new(io::ErrorKind::Other, "oh no!")) }
Error handling
А ако не са всичките грешки io::Error
?
Error handling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn all_your_numbers_are_belong_to_us() -> Result<i32, io::Error> {
let mut numbers = _try!(File::open("numbers.txt"));
let mut contents = String::new();
_try!(numbers.read_to_string(&mut contents));
let n =
match contents.lines().next() {
Some(first_line) => _try!(first_line.parse::<i32>()),
None => 0,
};
Ok(n)
}
fn main() {
println!("{:?}", all_your_numbers_are_belong_to_us());
}
error[E0308]: mismatched types --> src/bin/main_615375d9d3a9fb0ebdcc3469c41728aa6a42d4c9.rs:5:22 | 5 | Err(e) => return Err(e), | --- ^ expected struct `std::io::Error`, found struct `ParseIntError` | | | arguments to this enum variant are incorrect ... 19 | Some(first_line) => _try!(first_line.parse::<i32>()), | -------------------------------- in this macro invocation | note: tuple variant defined here --> /home/andrew/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:513:5 | 513 | Err(#[stable(feature = "rust1", since = "1.0.0")] E), | ^^^ = note: this error originates in the macro `_try` (in Nightly builds, run with -Z macro-backtrace for more info) For more information about this error, try `rustc --explain E0308`. error: could not compile `rust` due to previous error
macro_rules! _try { ($expr:expr) => { match $expr { Ok(result) => result, Err(e) => return Err(e), } } } use std::fs::File; use std::io::{self, Read}; fn all_your_numbers_are_belong_to_us() -> Result{ let mut numbers = _try!(File::open("numbers.txt")); let mut contents = String::new(); _try!(numbers.read_to_string(&mut contents)); let n = match contents.lines().next() { Some(first_line) => _try!(first_line.parse:: ()), None => 0, }; Ok(n) } fn main() { println!("{:?}", all_your_numbers_are_belong_to_us()); }
Error handling
1
2
3
4
5
6
7
8
9
10
11
12
13
struct FancyError { message: String }
impl From<io::Error> for FancyError {
fn from(e: io::Error) -> Self {
FancyError { message: format!("IO Error: {}", e) }
}
}
impl From<num::ParseIntError> for FancyError {
fn from(e: num::ParseIntError) -> Self {
FancyError { message: format!("ParseError: {}", e) }
}
}
Error handling
1
2
3
4
5
6
7
8
9
10
11
macro_rules! _try {
($expr:expr) => {
match $expr {
Ok(n) => n,
//Err(e) => return Err(e),
Err(e) => return Err(e.into()),
}
}
}
Error handling
1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn all_your_numbers_are_belong_to_us() -> Result<i32, FancyError> {
let mut numbers = _try!(File::open("numbers.txt"));
let mut contents = String::new();
_try!(numbers.read_to_string(&mut contents));
let n =
match contents.lines().next() {
Some(first_line) => _try!(first_line.parse::<i32>()),
None => 0,
};
Ok(n)
}
Error handling
- Statement-а
return Err(e.into());
изглежда по един и същ начин какъвто и да е конкретния return тип.
Error handling
- Statement-а
return Err(e.into());
изглежда по един и същ начин какъвто и да е конкретния return тип. let x: T = "foo".parse();
let y: U = "foo".parse();
Error handling
- Statement-а
return Err(e.into());
изглежда по един и същ начин какъвто и да е конкретния return тип. let x: T = "foo".parse();
let y: U = "foo".parse();
- И затова
_try!
е глобално използваем и в един момент просто влезе в стандартната библиотека катоtry!
- (Използваме
_try!
вместоtry!
в слайдовете, защотоtry
е запазена дума 😅)
Error handling
_try!
използва твърде много скоби и удивителни. И е deprecated.
1
2
3
4
5
6
7
8
9
10
fn all_your_quotes_are_belong_to_us() -> Result<String, io::Error> {
let mut deep = _try!(File::open("deep_quotes.txt"));
let mut wide = _try!(File::open("wide_quotes.txt"));
let mut contents = String::new();
_try!(deep.read_to_string(&mut contents));
_try!(wide.read_to_string(&mut contents));
Ok(contents)
}
Error handling
Има по-прост синтаксис:
1
2
3
4
5
6
7
8
9
10
fn all_your_quotes_are_belong_to_us() -> Result<String, io::Error> {
let mut deep = File::open("deep_quotes.txt")?;
let mut wide = File::open("wide_quotes.txt")?;
let mut contents = String::new();
deep.read_to_string(&mut contents)?;
wide.read_to_string(&mut contents)?;
Ok(contents)
}
Error handling
(Има и по-прост метод за четене на файлове:)
1
2
3
4
5
6
7
8
9
use std::fs;
use std::io;
fn all_your_quotes_are_belong_to_us() -> Result<String, io::Error> {
let mut quotes = String::new();
quotes.push_str(&fs::read_to_string("deep_quotes.txt")?);
quotes.push_str(&fs::read_to_string("wide_quotes.txt")?);
Ok(quotes)
}
use std::fs; use std::io; #[allow(dead_code)] fn all_your_quotes_are_belong_to_us() -> Result{ let mut quotes = String::new(); quotes.push_str(&fs::read_to_string("deep_quotes.txt")?); quotes.push_str(&fs::read_to_string("wide_quotes.txt")?); Ok(quotes) } fn main() { }
Error handling
методи върху Result
1
2
3
let mut fun = File::open("fun.txt").
or_else(|_error| File::open("passable.txt")).
or_else(|_error| File::open("okay_i_guess.txt"))?;
Error handling
методи върху Result
1
2
3
4
5
6
7
let optional_fun = File::open("fun.txt").
or_else(|_error| File::open("passable.txt")).
or_else(|_error| File::open("okay_i_guess.txt"));
if let Ok(mut fun) = optional_fun {
// Super-special Fun Time!
}
Error handling
методи върху Result
1
2
3
if let Err(_) = some_side_effects() {
// Едно warning-че, да се знае...
}
Error handling
методи върху Result
1
2
3
4
5
6
7
8
9
10
11
12
13
let number = "-13".parse::<i32>().unwrap();
let number = "foo".parse::<i32>().unwrap(); // BOOM!
let number = "-13".parse::<i32>().expect("BOOM!");
let number = "foo".parse::<i32>().expect("BOOM!"); // BOOM!
let number = "-13".parse::<i32>().unwrap_or(0);
let number = "foo".parse::<i32>().unwrap_or(0); // 👌
let number = "foo".parse::<i32>().unwrap_or_else(|e| {
println!("Warning: couldn't parse: {}", e);
0
});
Error handling
методи върху Result
1
2
3
4
5
6
let number = "foo".parse::<i32>();
println!("{:?}", number);
let number = "foo".parse::<i32>().
map_err(|e| format!("еее, {} ли бе", e));
println!("{:?}", number);
Err(ParseIntError { kind: InvalidDigit }) Err("еее, invalid digit found in string ли бе")
fn main() { let number = "foo".parse::(); println!("{:?}", number); let number = "foo".parse:: (). map_err(|e| format!("еее, {} ли бе", e)); println!("{:?}", number); }
- Ако не ни се занимава с conversion методи
Error handling
Още по-лесно: с trait object
1
2
3
4
5
6
7
8
9
10
11
use std::error::Error;
fn all_your_numbers_are_belong_to_us() -> Result<i32, Box<dyn Error>> {
let mut numbers = File::open("numbers.txt")?;
// ...
Ok(42)
}
fn main() {
println!("{:?}", all_your_numbers_are_belong_to_us());
}
Err(Os { code: 2, kind: NotFound, message: "No such file or directory" })
use std::fs::File; use std::error::Error; fn all_your_numbers_are_belong_to_us() -> Result> { let mut numbers = File::open("numbers.txt")?; // ... Ok(42) } fn main() { println!("{:?}", all_your_numbers_are_belong_to_us()); }
Error handling
Също толкова лесно и по-удобно: с anyhow!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// [dependencies]
// anyhow = "1.0"
use anyhow::anyhow;
fn all_your_numbers_are_belong_to_us() -> Result<i32, anyhow::Error> {
let mut numbers = File::open("numbers.txt")?;
// ...
Err(anyhow!("No need for conversion"))
}
fn main() {
println!("{:?}", all_your_numbers_are_belong_to_us());
}
Err(No such file or directory (os error 2))
use std::fs::File; // [dependencies] // anyhow = "1.0" use anyhow::anyhow; fn all_your_numbers_are_belong_to_us() -> Result{ let mut numbers = File::open("numbers.txt")?; // ... Err(anyhow!("No need for conversion")) } fn main() { println!("{:?}", all_your_numbers_are_belong_to_us()); }
Panic
Виждали сме panic:
Panic
Виждали сме panic:
panic!("something terrible happened")
Panic
Виждали сме panic:
panic!("something terrible happened")
assert_eq!(1, 2)
Panic
Виждали сме panic:
panic!("something terrible happened")
assert_eq!(1, 2)
None.unwrap()
Panic
Какво прави паниката
Panic
Какво прави паниката
- работи на ниво нишки
- терминира нишката в която е извикана и изписва съобщение за грешка
- unwind-ва стека (по подразбиране, може да се променя при компилация)
- при паника в главната нишка се прекратява цялата програма
Panic
Какво прави паниката
- работи на ниво нишки
- терминира нишката в която е извикана и изписва съобщение за грешка
- unwind-ва стека (по подразбиране, може да се променя при компилация)
- при паника в главната нишка се прекратява цялата програма
- паниките не могат да бъдат хванати (няма catch)
- (освен ако много, много не искаме, но това е за специални случаи)
Panic
Кога?
Panic
Кога?
- грешки в логиката на програмата
- (или при използване на библиотека)
Panic
Кога?
- грешки в логиката на програмата
- (или при използване на библиотека)
- които не зависят от user input
- и няма смисъл да се опитаме да се възстановим от тях
Panic
Кога?
- грешки в логиката на програмата
- (или при използване на библиотека)
- които не зависят от user input
- и няма смисъл да се опитаме да се възстановим от тях
"-13".parse::<u32>().unwrap()
Panic
Кога?
- грешки в логиката на програмата
- (или при използване на библиотека)
- които не зависят от user input
- и няма смисъл да се опитаме да се възстановим от тях
"-13".parse::<u32>().unwrap()
io::stdin().parse::<u32>()
-> любезно съобщение "please try again, but better"
Panic
Кога?
- грешки в логиката на програмата
- (или при използване на библиотека)
- които не зависят от user input
- и няма смисъл да се опитаме да се възстановим от тях
"-13".parse::<u32>().unwrap()
io::stdin().parse::<u32>()
-> любезно съобщение "please try again, but better"while let Err(_) = io::stdin().parse::<u32>() { ... }
Panic
Кога?
- грешки в логиката на програмата
- (или при използване на библиотека)
- които не зависят от user input
- и няма смисъл да се опитаме да се възстановим от тях
"-13".parse::<u32>().unwrap()
io::stdin().parse::<u32>()
-> любезно съобщение "please try again, but better"while let Err(_) = io::stdin().parse::<u32>() { ... }
- тестове
- примери
- rapid prototyping
Error handling
Обобщение
Грешките в rust се разделят на два вида
Error handling
Обобщение
Грешките в rust се разделят на два вида
- такива от които можем да се възстановим -
Result
,Option
, etc
Error handling
Обобщение
Грешките в rust се разделят на два вида
- такива от които можем да се възстановим -
Result
,Option
, etc - такива от които не можем да се възстановим -
panic!
Error handling
Обобщение
Грешките в rust се разделят на два вида
- такива от които можем да се възстановим -
Result
,Option
, etc - такива от които не можем да се възстановим -
panic!
unreachable!()
,todo!()
са от втория тип
Error handling
Обобщение
Грешките в rust се разделят на два вида
- такива от които можем да се възстановим -
Result
,Option
, etc - такива от които не можем да се възстановим -
panic!
unreachable!()
,todo!()
са от втория тип- няма exceptions!
Интересни пакети
- Fehler -- "exceptions" lol: https://github.com/withoutboats/fehler
- Eyre -- reporting: https://github.com/yaahc/eyre
- Скоро в стандартната библиотека, може би:
std::error::Report
- Скоро в стандартната библиотека, може би:
- Anyhow -- за приложения, лесни грешки: https://github.com/dtolnay/anyhow
- Thiserror -- за библиотеки: https://github.com/dtolnay/thiserror