10. Справяне с грешки

10. Справяне с грешки

10. Справяне с грешки

10 ноември 2017

Административни неща

Второ домашно! Днес!

Преговор

Конвертиране

From

impl From<Celsius> for Kelvin {
    fn from(t: Celsius) -> Kelvin {
        Kelvin(t.0 + 273.15)
    }
}

String parsing

String parsing

FromStr

trait FromStr {
    type Err;

    fn from_str(s: &str) -> Result<Self, Self::Err>;
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}

String parsing

FromStr

use std::str::FromStr;

let x = i32::from_str("-13");
let y = u8::from_str("323");
let z = f32::from_str("5e-3");


println!("{:?} {:?} {:?}", x, y, z);  // ?
Ok(-13)
Err(ParseIntError { kind: Overflow })
Ok(0.005) 

String parsing

parse

Има и по-ергономичен начин

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> { ... }
}

String parsing

parse

let x = "-13".parse::<i32>();
let y = "323".parse::<u8>();
let z = "5e-3".parse::<f32>();

println!("{:?} {:?} {:?}", x, y, z);
Ok(-13)
Err(ParseIntError { kind: Overflow })
Ok(0.005) 

String parsing

parse

use std::str::FromStr;

#[derive(Debug)]
struct Tardis {
    color: &'static str
}

String parsing

parse

impl FromStr for Tardis {
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s == "blue" {
            Ok(Tardis { color: "blue" })
        } else {
            Err("Everyboy knows the TARDIS is blue!")
        }
    }
}

String parsing

parse

fn main() {
    let t: Result<Tardis, <Tardis as FromStr>::Err> = "blue".parse();
    let x: Result<Tardis, <Tardis as FromStr>::Err> = "red".parse();

    println!("{:?}\n{:?}", t, x);
}

Ok(Tardis { color: "blue" })
Err("Everyboy knows the TARDIS is blue!")

String parsing

parse

fn main() {
    let t = "blue".parse::<Tardis>();
    let x = "red".parse::<Tardis>();

    println!("{:?}\n{:?}", t, x);
}

Ok(Tardis { color: "blue" })
Err("Everyboy knows the TARDIS is blue!")

Error handling

Error handling

use std::fs::File;
use std::io::Read;

fn main() {
    let mut file = File::open("deep_quotes.txt"); // Spoiler: няма да го бъде

    let mut contents = String::new();
    file.read_to_string(&mut contents);

    println!("{}", contents);
}

Error handling

error[E0599]: no method named `read_to_string` found for
type `std::result::Result` in the current scope
--> src/main.rs:8:10
  |
8 |     file.read_to_string(&mut contents);
  |          ^^^^^^^^^^^^^^

Error handling

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

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!("OH, NO! {}", e),
    };

    let mut contents = String::new();
    file.read_to_string(&mut contents);
    println!("{}", contents);
}

// => Failure is just success rounded down, my friend!

Error handling

fn main() {
    let mut deep = match File::open("deep_quotes.txt") {
        Ok(f) => f,
        Err(e) => panic!("OH, NO! {}", e),
    };
    let mut wide = match File::open("wide_quotes.txt") {
        Ok(f) => f,
        Err(e) => panic!("OH, NO! {}", e),
    };

    let mut contents = String::new();
    deep.read_to_string(&mut contents);
    wide.read_to_string(&mut contents);
    println!("{}", contents);
}

Error handling

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!("OH, NO! {}", e),
    };
    let mut wide = match File::open("wide_quotes.txt") {
        Ok(f) => f,
        Err(e) => panic!("OH, NO! {}", e),
    };

    let mut contents = String::new();
    deep.read_to_string(&mut contents);
    wide.read_to_string(&mut contents);
    println!("{}", contents);
}

Error handling

fn main() {
    match all_your_quotes_are_belong_to_us() {
        Ok(contents) => println!("{}", contents),
        Err(e) => panic!("OH, NO! {}", 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);
    wide.read_to_string(&mut contents);
    Ok(contents)
}

Error handling

A wild macro appears

macro_rules! try {

    ($expr:expr) => {
        match $expr {
            Ok(result) => result,
            Err(e) => return Err(e),
        }
    }

}

Error handling

fn main() {
    match all_your_quotes_are_belong_to_us() {
        Ok(contents) => println!("{}", contents),
        Err(e) => panic!("OH, NO! {}", 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);
    wide.read_to_string(&mut contents);

    Ok(contents)
}

Error handling

warning: unused `std::result::Result` which must be used
  --> src/main.rs:16:5
   |
16 |     deep.read_to_string(&mut contents);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: #[warn(unused_must_use)] on by default

warning: unused `std::result::Result` which must be used
  --> src/main.rs:17:5
   |
17 |     wide.read_to_string(&mut contents);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    Finished dev [unoptimized + debuginfo] target(s) in 0.42 secs
     Running `target/debug/scratch`

Error handling

fn main() {
    match all_your_quotes_are_belong_to_us() {
        Ok(contents) => println!("{}", contents),
        Err(e) => panic!("OH, NO! {}", 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

Въпрос

// ??

fn main() {
    try!(all_your_quotes_are_belong_to_us());
}

Error handling

error[E0308]: mismatched types
 --> src/main.rs:5:5
  |
5 |     try!(all_your_quotes_are_belong_to_us());
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
  |
  = note: expected type `()`
             found type `std::result::Result<_, _>` 

Error handling

А ако не са всичките грешки io::Error?

Error handling

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)
}

Error handling

error[E0308]: mismatched types
  --> src/main.rs:10:34
   |
10 |             Err(e) => return Err(e),
   |                                  ^ expected struct `std::io::Error`,
                                        found struct `std::num::ParseIntError`
...
47 |             try!(first_line.parse::<i32>())
   |             ------------------------------- in this macro invocation
   |
   = note: expected type `std::io::Error`
              found type `std::num::ParseIntError`

Error handling

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

macro_rules! try {

    ($expr:expr) => {
        match $expr {
            Ok(n) => n,
            //Err(e) => return Err(e),
            Err(e) => return Err(e.into()),
        }
    }

}

Error handling

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

fn main() {
    match all_your_numbers_are_belong_to_us() {
        Ok(n) => println!("{}", n),
        Err(e) => panic!("OH, NO! {}", e.message),
    }
}

Error handling

Твърде много скоби и удивителни. Also, deprecated.

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

Има по-прост синтаксис:

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

методи върху Result

let mut fun = File::open("fun.txt").
    or_else(|_| File::open("passable.txt")).
    or_else(|_| File::open("okay_i_guess.txt"))?;

Error handling

методи върху Result

let optional_fun = File::open("fun.txt").
    or_else(|_| File::open("passable.txt")).
    or_else(|_| File::open("okay_i_guess.txt"));

if let Ok(mut fun) = optional_fun {
    // Super-special Fun Time!
}

Error handling

методи върху Result

if let Err(_) = some_side_effects() {
    // Едно warning-че, да се знае...
}

Error handling

методи върху Result

let number = "-13".parse::<i32>().unwrap();
let number = "foo".parse::<i32>().unwrap(); // 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
});

Panic

Panic

Какво прави паниката

Panic

Кога?

Error handling

Обобщение

Грешките в rust се разделят на два вида

Read & Write

Има стандартни типажи, които ни помагат за четене и писане

Read & Write

Read

Един от тях е Read

pub trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;

    unsafe fn initializer(&self) -> Initializer { ... }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { ... }
    fn read_to_string(&mut self, buf: &mut String) -> Result<usize> { ... }
    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { ... }
    fn by_ref(&mut self) -> &mut Self where Self: Sized { ... }
    fn bytes(self) -> Bytes<Self> where Self: Sized { ... }
    fn chars(self) -> Chars<Self> where Self: Sized { ... }
    fn chain<R: Read>(self, next: R) -> Chain<Self, R> where Self: Sized { ... }
    fn take(self, limit: u64) -> Take<Self> where Self: Sized { ... }
}

Read

Имплементира се за някои очаквани структури

impl Read for File
impl Read for Stdin
impl Read for TcpStream

Read

Видяхме как може да четем от файл, а сега и от Stdin

use std::io::{self, Read};

let mut buffer = String::new();
io::stdin().read_to_string(&mut buffer)?;

Read & Write

Write

За писане се използва Write

pub trait Write {
    fn write(&mut self, buf: &[u8]) -> Result<usize>;
    fn flush(&mut self) -> Result<()>;

    fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... }
    fn write_fmt(&mut self, fmt: Arguments) -> Result<()> { ... }
    fn by_ref(&mut self) -> &mut Self where Self: Sized { ... }
}

Write

Както Read се имплементира за очаквани структури

impl Write for File
impl Write for Stdout
impl Write for Stderr
impl Write for TcpStream
impl Write for Vec<u8>

Write

use std::fs::File;
use std::io::Write;

let mut f = File::create("foo.txt")?;
f.write_all(b"Hello, world!")?;

Read & Write

Като цяло са интуитивни, но не винаги ефективни когато правим много, но малки операции

BufReader & BufWriter

BufReader & BufWriter

BufReader

BufReader е wrapper за структури, които имплементират Read

use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;

let f = File::open("log.txt")?;
let mut reader = BufReader::new(f);

let mut line = String::new();
let len = reader.read_line(&mut line)?;
println!("First line is {} bytes long", len);

BufReader & BufWriter

BufReader

Тук се появява нов метод read_line

BufReader & BufWriter

BufRead

Ето от къде

pub trait BufRead: Read {
    fn fill_buf(&mut self) -> Result<&[u8]>;
    fn consume(&mut self, amt: usize);

    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize> { ... }
    fn read_line(&mut self, buf: &mut String) -> Result<usize> { ... }
    fn split(self, byte: u8) -> Split<Self> where Self: Sized { ... }
    fn lines(self) -> Lines<Self> where Self: Sized { ... }
}

BufReader & BufWriter

BufWriter

Подобно, BufWriter е wrapper за структури, които имплементират Write

use std::io::prelude::*;
use std::io::BufWriter;
use std::net::TcpStream;

let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());

for i in 1..10 {
    stream.write(&[i]).unwrap();
}

В този пример чрез BufWriter превръщаме 10 system calls в 1

BufReader & BufWriter

BufWrite

Сори, няма BufWrite :(

Read & Write

Добре де, това ли е всичко?

Read & Write

Не

Read & Write

Write може да се използва и за тестване чрез mock

// Представете си, че пишем u32 и функцията не е толкова безполезна
fn write_u8<W>(writer: &mut W, data: u8) -> io::Result<usize> where W: Write {
    writer.write(&[data])
}

#[test]
fn test_write_u8() {
    let mut mock: Vec<u8> = Vec::new();

    write_u8(&mut mock, 42).unwrap();

    assert_eq!(mock.len(), 1);
    assert_eq!(mock[0], 42);
}

Any

use std::any::Any;

let s = String::from("foo");
let v: Vec<&Any> = vec![&10_i32, &s, &1.23];

for elem in v {
    if let Some(num) = elem.downcast_ref::<i32>() {
        println!("number: {}", num);
    }
    else if let Some(s) = elem.downcast_ref::<String>() {
        println!("string: {}", s);
    }
    else {
        println!("unknown: {:?}", elem);
    }
}

Въпроси