Справяне с грешки
25 октомври 2018
Административни неща
Административни неща
- Първо домашно приключи, на печелившите честито
Административни неща
- Първо домашно приключи, на печелившите честито
- Първо предизвикателство -- скоро
Преговор
Преговор
- Generics (мономорфизация)
Преговор
- Generics (мономорфизация)
- Traits (като интерфейси, необходими за практическа употреба на generics)
Преговор
- Generics (мономорфизация)
- Traits (като интерфейси, необходими за практическа употреба на generics)
- Асоциирани типове в trait-ове
Домашно 1
Домашно 1
- Редовния проблем с unicode:
to_lowercase
,to_uppercase
, etc.
Домашно 1
- Редовния проблем с unicode:
to_lowercase
,to_uppercase
, etc. - Валидност и конструиране: поотделно или наведнъж? (Приятно четиво по темата: pdf)
Домашно 1
- Редовния проблем с unicode:
to_lowercase
,to_uppercase
, etc. - Валидност и конструиране: поотделно или наведнъж? (Приятно четиво по темата: pdf)
- Ах, този
unwrap
…
Домашно 1
- Редовния проблем с unicode:
to_lowercase
,to_uppercase
, etc. - Валидност и конструиране: поотделно или наведнъж? (Приятно четиво по темата: pdf)
- Ах, този
unwrap
… - Error handling обикновено не се прави с
None
, но за това след малко.
Конвертиране
Конвертиране
struct Celsius(f64);
struct Fahrenheit(f64);
struct Kelvin(f64);
fn room_temperature() -> Fahrenheit {
Fahrenheit(68.0)
}
Конвертиране
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
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
Сега вече можем да си сварим яйца
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
pub trait From<T> {
fn from(T) -> Self;
}
Конвертиране
From
pub trait From<T> {
fn from(T) -> Self;
}
From<T> for U
конвертира отT
доU
From<T> for T
е имплементирано автоматично- Конвертирането не може да се провали
Конвертиране
Into
U::from(t)
е дълго за писане- Затова съществува "реципрочен" метод
pub trait Into<T> {
fn into(self) -> T;
}
Конвертиране
Into
U::from(t)
е дълго за писане- Затова съществува "реципрочен" метод
pub trait Into<T> {
fn into(self) -> T;
}
Into<U> for T
конвертира отT
доU
Конвертиране
Into
U::from(t)
е дълго за писане- Затова съществува "реципрочен" метод
pub trait Into<T> {
fn into(self) -> T;
}
Into<U> for T
конвертира отT
доU
Into<T> for T
е имплементирано автоматично
Конвертиране
Into
U::from(t)
е дълго за писане- Затова съществува "реципрочен" метод
pub trait Into<T> {
fn into(self) -> T;
}
Into<U> for T
конвертира отT
доU
Into<T> for T
е имплементирано автоматичноInto<U> for T
се имплементира автоматично като имплементирамеFrom<T> for U
- Практиката е ръчно да се имплементира
From
Конвертиране
Into
// използвайки 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
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);
String parsing
String parsing
- Ами ако искаме да създадем обект от низ? (от JSON низ, от XML)
String parsing
- Ами ако искаме да създадем обект от низ? (от JSON низ, от XML)
- Не можем да използваме
From
, защото не сме сигурни че създаването ще е успешно - Има специален trait за това
String parsing
FromStr
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
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: 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> { ... }
}
- Типа
<F as FromStr>::Err
е "типаErr
, който е дефиниран заFromStr
имплементацията наF
"
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> { ... }
}
- Типа
<F as FromStr>::Err
е "типаErr
, който е дефиниран заFromStr
имплементацията наF
" - Generic параметъра F трябва да имплементира
FromStr
(подобно наInto
заFrom
)
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> { ... }
}
- Типа
<F as FromStr>::Err
е "типаErr
, който е дефиниран заFromStr
имплементацията наF
" - Generic параметъра F трябва да имплементира
FromStr
(подобно наInto
заFrom
) - Метода
parse
е имплементиран върхуstr
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> { ... }
}
- Типа
<F as FromStr>::Err
е "типаErr
, който е дефиниран заFromStr
имплементацията наF
" - Generic параметъра F трябва да имплементира
FromStr
(подобно наInto
заFrom
) - Метода
parse
е имплементиран върхуstr
- Метода
parse
е generic по return стойността си
String parsing
parse
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: Overflow }) Ok(0.005)
String parsing
parse
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: Overflow }) Ok(0.005)
String parsing
parse
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: Overflow }) Ok(0.005)
String parsing
parse
use std::str::FromStr;
#[derive(Debug)]
struct Potato {
is_a_potato: bool,
}
String parsing
parse
impl FromStr for Potato {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "картоф" {
Ok(Potato { is_a_potato: true })
} else {
Err(String::from("what is this even"))
}
}
}
String parsing
parse
fn main() {
let p1: Result<Potato, _> = "картоф".parse();
let p2: Result<Potato, _> = "патладжан".parse();
println!("{:?}\n{:?}", p1, p2);
}
Ok(Potato { is_a_potato: true }) Err("what is this even")
String parsing
parse
fn main() {
let p1 = "картоф".parse::<Potato>();
let p2 = "патладжан".parse::<Potato>();
println!("{:?}\n{:?}", p1, p2);
}
Ok(Potato { is_a_potato: true }) Err("what is this even")
Error handling
Error handling
use std::fs::File;
use std::io::Read;
fn main() {
let mut file = File::open("data/deep_quotes.txt");
let mut contents = String::new();
file.read_to_string(&mut contents);
println!("{}", contents);
}
Error handling
use std::fs::File;
use std::io::Read;
fn main() {
let mut file = File::open("data/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 type `std::result::Result<std::fs::File, std::io::Error>` in the current scope --> /src/main_7.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("data/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!
Error handling
use std::fs::File;
use std::io::Read;
fn main() {
let mut file = match File::open("data/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/main_9.rs:7:19 note: Run with `RUST_BACKTRACE=1` for a backtrace.
Error handling
fn main() {
let mut deep = match File::open("data/deep_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut wide = match File::open("data/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 !
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("data/deep_quotes.txt") {
Ok(f) => f,
Err(e) => panic!("😞 {}", e),
};
let mut wide = match File::open("data/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 !
Error handling
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("data/deep_quotes.txt") {
Ok(f) => f,
Err(e) => return Err(e),
};
let mut wide = match File::open("data/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 !
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!("😞 {}", e),
}
}
fn all_your_quotes_are_belong_to_us() -> Result<String, io::Error> {
let mut deep = try!(File::open("data/deep_quotes.txt"));
let mut wide = try!(File::open("data/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 !
Error handling
А без онзи unwrap
?
fn main() {
let mut file = match File::open("data/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
?
fn main() {
let mut file = match File::open("data/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 `std::result::Result` which must be used --> /src/main_14.rs:10:5 | 10 | 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
Error handling
А без онзи unwrap
?
fn main() {
let mut file = match File::open("data/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!
Error handling
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("data/deep_quotes.txt"));
let mut wide = try!(File::open("data/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
Въпрос
fn main() {
try!(all_your_quotes_are_belong_to_us());
}
error[E0308]: mismatched types --> /src/main_16.rs:6:5 | 6 | try!(all_your_quotes_are_belong_to_us()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result` | = note: expected type `()` found type `std::result::Result<_, _>` = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
Error handling
Въпрос
fn main() -> Result<(), String> {
try!(all_your_quotes_are_belong_to_us());
println!("This is okay");
Ok(())
}
Bla-bla failure success This is okay
Error handling
Въпрос
fn main() -> Result<(), String> {
try!(all_your_quotes_are_belong_to_us());
Err(String::from("All your Err are belong to us"))
}
Bla-bla failure success Error: "All your Err are belong to us"
Error handling
А ако не са всичките грешки io::Error?
Error handling
fn all_your_numbers_are_belong_to_us() -> Result<i32, io::Error> {
let mut numbers = try!(File::open("data/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[E0277]: the trait bound `std::io::Error: std::convert::From<std::num::ParseIntError>` is not satisfied --> /src/main_19.rs:11:33 | 11 | Some(first_line) => try!(first_line.parse::<i32>()), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<std::num::ParseIntError>` is not implemented for `std::io::Error` | = help: the following implementations were found: <std::io::Error as std::convert::From<std::io::ErrorKind>> <std::io::Error as std::convert::From<std::ffi::NulError>> <std::io::Error as std::convert::From<std::io::IntoInnerError<W>>> = note: required by `std::convert::From::from` = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
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
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
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
try!
използва твърде много скоби и удивителни. И е deprecated.
fn all_your_quotes_are_belong_to_us() -> Result<String, io::Error> {
let mut deep = try!(File::open("data/deep_quotes.txt"));
let mut wide = try!(File::open("data/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("data/deep_quotes.txt")?;
let mut wide = File::open("data/wide_quotes.txt")?;
let mut contents = String::new();
deep.read_to_string(&mut contents)?;
wide.read_to_string(&mut contents)?;
Ok(contents)
}
Error handling
(Има и по-прост метод за четене на файлове:)
fn all_your_quotes_are_belong_to_us() -> Result<String, io::Error> {
let mut quotes = String::new();
quotes.push_str(&fs::read_to_string("data/deep_quotes.txt")?);
quotes.push_str(&fs::read_to_string("data/wide_quotes.txt")?);
Ok(quotes)
}
Error handling
методи върху Result
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
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
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>().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
});
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
Какво прави паниката
- работи на ниво нишки
Panic
Какво прави паниката
- работи на ниво нишки
- терминира нишката в която е извикана и изписва съобщение за грешка
Panic
Какво прави паниката
- работи на ниво нишки
- терминира нишката в която е извикана и изписва съобщение за грешка
- unwind-ва стека (по подразбиране, може да се променя при компилация)
Panic
Какво прави паниката
- работи на ниво нишки
- терминира нишката в която е извикана и изписва съобщение за грешка
- unwind-ва стека (по подразбиране, може да се променя при компилация)
- при паника в главната нишка се прекратява цялата програма
Panic
Какво прави паниката
- работи на ниво нишки
- терминира нишката в която е извикана и изписва съобщение за грешка
- unwind-ва стека (по подразбиране, може да се променя при компилация)
- при паника в главната нишка се прекратява цялата програма
- паниките не могат да бъдат хванати (няма catch)
Panic
Какво прави паниката
- работи на ниво нишки
- терминира нишката в която е извикана и изписва съобщение за грешка
- unwind-ва стека (по подразбиране, може да се променя при компилация)
- при паника в главната нишка се прекратява цялата програма
- паниките не могат да бъдат хванати (няма catch)
- (освен ако много, много не искаме, но това е за специални случаи)
Panic
Кога?
Panic
Кога?
- грешки в логиката на програмата
- (или при използване на библиотека)
Panic
Кога?
- грешки в логиката на програмата
- (или при използване на библиотека)
- които не зависят от user input
- и няма смисъл да се опитаме да се възстановим от тях
Panic
Кога?
- грешки в логиката на програмата
- (или при използване на библиотека)
- които не зависят от user input
- и няма смисъл да се опитаме да се възстановим от тях
- тестове
- примери
- 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!()
,unimplemented!()
са от втория тип
Error handling
Обобщение
Грешките в rust се разделят на два вида
- такива от които можем да се възстановим -
Result
,Option
, etc - такива от които не можем да се възстановим -
panic!
unreachable!()
,unimplemented!()
са от втория тип- няма exceptions!
Read & Write
Има стандартни типажи, които ни помагат за четене и писане
Read & Write
std::io::Read
Един от тях е Read
pub trait Read {
// Required:
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
// Provided:
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { ... }
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { ... }
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { ... }
fn by_ref(&mut self) -> &mut Self where Self: Sized { ... }
fn bytes(self) -> Bytes<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 { ... }
// +nightly:
unsafe fn initializer(&self) -> Initializer { ... }
fn chars(self) -> Chars<Self> where Self: Sized { ... }
}
Read & Write
Бележка:
В модула std::io
има следната дефиниция:
type Result<T> = Result<T, Error>;
Този синтаксис дефинира type alias ("тип-синоним"). Типа std::io::Result<T>
е еквивалентен на Result<T, std::io::Error>
.
Това се използва за улеснение, и спокойно може да use-нем std::io
и да адресираме io::Result<usize>
без да указваме типа за грешка -- той вече е конкретизиран в alias-а.
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
std::io::Write
За писане се използва Write
pub trait Write {
// Required:
fn write(&mut self, buf: &[u8]) -> io::Result<usize>;
fn flush(&mut self) -> io::Result<()>;
// Provided:
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { ... }
fn write_fmt(&mut self, fmt: Arguments) -> io::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
иBufWriter
BufReader & BufWriter
- Затова са създадени
BufReader
иBufWriter
- Използват се да буферират операциите, както се досещате от имената им
BufReader & BufWriter
std::io::BufReader
BufReader
е wrapper за структури, които имплементират Read
use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;
fn main() -> Result<(), std::io::Error> {
let f = File::open("data/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);
Ok(())
}
First line is 11 bytes long
BufReader & BufWriter
std::io::BufRead
Тук се появява нов метод read_line
:
pub trait BufRead: Read {
// Required:
fn fill_buf(&mut self) -> io::Result<&[u8]>;
fn consume(&mut self, amt: usize);
// Provided:
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> { ... }
fn read_line(&mut self, buf: &mut String) -> io::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
:(
BufReader & BufWriter
Read
: Trait, който е имплементиран от файлове, сокети, и т.н., за четене на брой байтове.BufReader
: Структура, която обвива нещо, което еRead
, която също eRead
и която имплементираBufRead
.BufRead
: Trait за структури катоBufReader
, които четат, буферирайки.
BufReader & BufWriter
Read
: Trait, който е имплементиран от файлове, сокети, и т.н., за четене на брой байтове.BufReader
: Структура, която обвива нещо, което еRead
, която също eRead
и която имплементираBufRead
.BufRead
: Trait за структури катоBufReader
, които четат, буферирайки.
Write
: Trait, който е имплементиран от файлове, сокети, и т.н., за писане на брой байтове.BufWriter
: Структура, която обвива нещо, което еWrite
, която също eWrite
и която позволява буферирано писанеBufWrite
: Не съществува :/.
Read & Write
Write
може да се използва и за тестване чрез mock
fn write_u8<W>(writer: &mut W, data: u8) -> io::Result<usize>
where W: Write {
// Do cool stuff with `writer`
}
#[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);
}