Enums & Pattern-matching
("Изброени типове" и "съпоставяне на образци")
18 октомври 2022
Преговор
Преговор
- Структури (асоциирани функции, методи)
Преговор
- Структури (асоциирани функции, методи)
self
,&self
,&mut self
Преговор
- Структури (асоциирани функции, методи)
self
,&self
,&mut self
- Организация на кода в модули
Преговор
- Структури (асоциирани функции, методи)
self
,&self
,&mut self
- Организация на кода в модули
- Видимост (
pub
за типове, полета, функции)
Enums
- Скучно име, идващо от C, където са доста ограничени
Enums
- Скучно име, идващо от C, където са доста ограничени
- По-готини имена са "algebraic datatype" и "sum type"
Enums
- Скучно име, идващо от C, където са доста ограничени
- По-готини имена са "algebraic datatype" и "sum type"
- Също е известно като "tagged union"
Enums
- Скучно име, идващо от C, където са доста ограничени
- По-готини имена са "algebraic datatype" и "sum type"
- Също е известно като "tagged union"
enum IpAddrKind {
V4,
V6,
}
Enums
Инстанциране
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
Enums
Параметър
fn route(ip_type: IpAddrKind) { }
route(IpAddrKind::V4);
route(IpAddrKind::V6);
Enums
Данни
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
Enums
Данни
По-удобен и четим начин
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
Enums
Данни
Може да спестим памет като знаем че IPv4
използва стойности от 0-255
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
Enums
Защо?
Enums
Защо?
- Повечето типове са "контейнери" -- struct, list, tuple
Enums
Защо?
- Повечето типове са "контейнери" -- struct, list, tuple
- Enum ви позволява да изразите "екслузивност" -- или един пакет данни, или друг
Enums
Защо?
- Повечето типове са "контейнери" -- struct, list, tuple
- Enum ви позволява да изразите "екслузивност" -- или един пакет данни, или друг
struct IpAddr {
v4: (u8, u8, u8, u8),
v6: String,
}
// Warning: фалшив Rust код, няма NULL
let home = IpAddr { v4: (127, 0, 0, 1), v6: NULL };
let loopback = IpAddr { v4: NULL, v6: String::from("::1") };
Не е особено ясно -- човек трябва "да се усети" като чете кода.
Enums
Варианти
enum Message {
Quit,
Move { x: i64, y: i64 },
Write(String),
ChangeColor(i64, i64, i64),
}
Message::Quit;
Message::Move { x: 3, y: 4 };
Message::Write(String::from("baba"));
Message::ChangeColor(255, 0, 0);
Enum варианти като структури
struct QuitMessage; // unit struct
struct MoveMessage {
x: i64,
y: i64,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i64, i64, i64); // tuple struct
QuitMessage;
MoveMessage { x: 3, y: 4 };
WriteMessage(String::from("baba"));
ChangeColorMessage(255, 0, 0);
Методи
enum Message { ... }
impl Message {
fn call(&self) {
// ...
}
}
let m = Message::Write(String::from("hello"));
m.call();
Методи
enum Message { ... }
impl Message {
fn call(&self) {
// ...
}
}
let m = Message::Write(String::from("hello"));
m.call();
А какво точно правим вътре в call
? Ще видим след няколко слайда :)
Разполагане в паметта
Вариант | Памет | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
8B | 24B | ||||||||||||
Quit | 0 | ||||||||||||
Move { x: i64, y: i64 } | 1 | i64 | i64 | ||||||||||
Write(String) | 2 | String | |||||||||||
ChangeColor(i64, i64, i64) | 3 | i64 | i64 | i64 |
- 8 байта за дискриминанта
- 24 байта за данните
Без дискриминанта
use std::mem;
enum Basic {
A,
B,
}
fn main() {
println!("{:?}", mem::size_of::<Basic>());
}
1
use std::mem; enum Basic { A, B, } fn main() { println!("{:?}", mem::size_of::()); }
Null-pointer optimization
use std::mem;
enum LeanAndMean {
A,
B(String),
}
fn main() {
println!("Without enum: {:?}", mem::size_of::<String>());
println!("With enum: {:?}", mem::size_of::<LeanAndMean>());
}
Without enum: 24 With enum: 24
use std::mem; enum LeanAndMean { A, B(String), } fn main() { println!("Without enum: {:?}", mem::size_of::()); println!("With enum: {:?}", mem::size_of:: ()); }
Null-pointer optimization
use std::mem;
enum JustMean {
A,
B(String),
C,
}
fn main() {
println!("Without enum: {:?}", mem::size_of::<String>());
println!("With enum: {:?}", mem::size_of::<JustMean>());
}
Without enum: 24 With enum: 32
use std::mem; enum JustMean { A, B(String), C, } fn main() { println!("Without enum: {:?}", mem::size_of::()); println!("With enum: {:?}", mem::size_of:: ()); }
Енума Option
Енума Option
- Понякога искаме да изразим липсваща стойност.
Енума Option
- Понякога искаме да изразим липсваща стойност.
- Проблема обикновено се решава със специалната стойност
NULL
.
Енума Option
- Понякога искаме да изразим липсваща стойност.
- Проблема обикновено се решава със специалната стойност
NULL
. - Това води до готини неща като "тройни булеви стойности" и любимите на всички null pointer exceptions.
Енума Option
- Понякога искаме да изразим липсваща стойност.
- Проблема обикновено се решава със специалната стойност
NULL
. - Това води до готини неща като "тройни булеви стойности" и любимите на всички null pointer exceptions.
- Тук-таме
NULL
се използва и за "имаше грешка, но¯\_(ツ)_/¯
каква"
Енума Option
- Понякога искаме да изразим липсваща стойност.
- Проблема обикновено се решава със специалната стойност
NULL
. - Това води до готини неща като "тройни булеви стойности" и любимите на всички null pointer exceptions.
- Тук-таме
NULL
се използва и за "имаше грешка, но¯\_(ツ)_/¯
каква" - В Rust няма
NULL
!
Енума Option
Option има 2 стойности:
Some(val)
None
Енума Option
let some_number = Some(5);
let some_string = Some("string");
let absent_number: Option<i32> = None;
println!("{:?}", some_number);
println!("{:?}", some_string);
println!("{:?}", absent_number);
Some(5) Some("string") None
fn main() { let some_number = Some(5); let some_string = Some("string"); let absent_number: Option= None; println!("{:?}", some_number); println!("{:?}", some_string); println!("{:?}", absent_number); }
Енума Option
let some_number: Option<u32> = Some(5);
let some_string: Option<&str> = Some("string");
let absent_number: Option<i32> = None;
println!("{:?}", some_number);
println!("{:?}", some_string);
println!("{:?}", absent_number);
Some(5) Some("string") None
fn main() { let some_number: Option= Some(5); let some_string: Option<&str> = Some("string"); let absent_number: Option = None; println!("{:?}", some_number); println!("{:?}", some_string); println!("{:?}", absent_number); }
Pattern Matching
"Съпоставяне на образци"
Pattern Matching
"Съпоставяне на образци"
- Идея идваща от функционалното програмиране
Pattern Matching
"Съпоставяне на образци"
- Идея идваща от функционалното програмиране
- Може да се ползва с енуми и стойности в тях
Pattern Matching
"Съпоставяне на образци"
- Идея идваща от функционалното програмиране
- Може да се ползва с енуми и стойности в тях
- Използва се чрез
match
оператора
Pattern Matching
let x = Some(42_u32);
match x {
Some(val) => println!("Value: {}", val),
None => println!("No value found"),
}
Value: 42
fn main() { let x = Some(42_u32); match x { Some(val) => println!("Value: {}", val), None => println!("No value found"), } }
Pattern Matching
let x: Option<u32> = None;
match x {
Some(val) => println!("Value: {}", val),
None => println!("No value found"),
}
No value found
fn main() { let x: Option= None; match x { Some(val) => println!("Value: {}", val), None => println!("No value found"), } }
Pattern Matching
match
може да върне стойност:
let x = Some(4);
let y = match x {
Some(val) => Some(val * val),
None => None,
};
println!("{:?}", y);
Some(16)
fn main() { let x = Some(4); let y = match x { Some(val) => Some(val * val), None => None, }; println!("{:?}", y); }
Pattern Matching
match
може да върне стойност:
let x = Some(4);
let y = match x {
Some(val) => val * val,
None => 0,
};
println!("{:?}", y);
16
fn main() { let x = Some(4); let y = match x { Some(val) => val * val, None => 0, }; println!("{:?}", y); }
Pattern Matching
match
може да излезе от функцията
let y = match x {
Some(val) => val * val,
None => return None,
};
Pattern Matching
match
може да съдържа блокове от код:
let y = match x {
Some(val) => {
println!("Will return {}", val * val);
Some(val * val)
},
None => {
println!("Will do nothing!!");
None
},
};
Pattern Matching
Задължително трябва да се покрият всички случаи!
let x = Some(3);
let y = match x {
Some(i) => Some(i + 1),
};
error[E0004]: non-exhaustive patterns: `None` not covered --> src/bin/main_95c8682b7401bcccd982dc5ffc0ed7d72379cdae.rs:4:15 | 4 | let y = match x { | ^ pattern `None` not covered | note: `Option<i32>` defined here --> /home/andrew/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:522:5 | 518 | pub enum Option<T> { | ------------------ ... 522 | None, | ^^^^ not covered = note: the matched value is of type `Option<i32>` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | 5 ~ Some(i) => Some(i + 1), 6 ~ None => todo!(), | For more information about this error, try `rustc --explain E0004`. error: could not compile `rust` due to previous error
fn main() { let x = Some(3); let y = match x { Some(i) => Some(i + 1), }; }
Pattern Matching
Работи и с прости стойности: _
означава всичко останало
match x {
69 => println!("Nice."),
666 => println!(r"\m/"),
_ => println!(r"¯\_(ツ)_/¯"),
}
Pattern Matching
НО! Ръкавите трябва да са от един и същ тип
let response = match x {
69 => "Nice!",
666 => r"\m/",
_ => 0_0,
};
error[E0308]: `match` arms have incompatible types --> src/bin/main_48ccf2f6abbea81f52e3d53a72c573d98dd05a43.rs:6:12 | 3 | let response = match x { | ________________- 4 | | 69 => "Nice!", | | ------- this is found to be of type `&str` 5 | | 666 => r"\m/", | | ------ this is found to be of type `&str` 6 | | _ => 0_0, | | ^^^ expected `&str`, found integer 7 | | }; | |_- `match` arms have incompatible types For more information about this error, try `rustc --explain E0308`. error: could not compile `rust` due to previous error
fn main() { let x = 3; let response = match x { 69 => "Nice!", 666 => r"\m/", _ => 0_0, }; }
More control flow
if let
Понякога да използваме match
за един случай и да покрием всички други с _
е прекалено много код
let some_value = Some(8);
match some_value {
Some(8) => println!("8)"),
_ => (),
}
8)
fn main() { let some_value = Some(8); match some_value { Some(8) => println!("8)"), _ => (), } }
More control flow
if let
Запознайте се с if let
:
let some_value = Some(8);
if let Some(8) = some_value {
println!("::::)");
}
::::)
fn main() { let some_value = Some(8); if let Some(8) = some_value { println!("::::)"); } }
More control flow
while let
А защо не и while let
:
let so_eighty = [8, 8, 8, 88, 8];
let mut iter8or = so_eighty.iter();
while let Some(8) = iter8or.next() {
println!("∞");
}
∞ ∞ ∞
fn main() { let so_eighty = [8, 8, 8, 88, 8]; let mut iter8or = so_eighty.iter(); while let Some(8) = iter8or.next() { println!("∞"); } }
Итерация
… и някои леко неинтуитивни изненади
let numbers = [1, 2, 3].iter(); // std::slice::Iter
let chars = "abc".chars(); // std::str::Chars
let words = "one two three".split_whitespace(); // std::str::SplitWhitespace
Итерация
let numbers = [1, 2, 3].iter(); // std::slice::Iter
let chars = "abc".chars(); // std::str::Chars
let words = "one two three".split_whitespace(); // std::str::SplitWhitespace
println!("{:?}", numbers);
println!("{:?}", chars);
println!("{:?}", words);
Iter([1, 2, 3]) Chars(['a', 'b', 'c']) SplitWhitespace { inner: Filter { iter: Split(SplitInternal { start: 0, end: 13, matcher: CharPredicateSearcher { haystack: "one two three", char_indices: CharIndices { front_offset: 0, iter: Chars(['o', 'n', 'e', ' ', 't', 'w', 'o', ' ', 't', 'h', 'r', 'e', 'e']) } }, allow_trailing_empty: true, finished: false }) } }
fn main() { let numbers = [1, 2, 3].iter(); // std::slice::Iter let chars = "abc".chars(); // std::str::Chars let words = "one two three".split_whitespace(); // std::str::SplitWhitespace println!("{:?}", numbers); println!("{:?}", chars); println!("{:?}", words); }
Итерация
let numbers: Vec<&u32> = [1, 2, 3].iter().collect();
let chars: Vec<char> = "abc".chars().collect();
let words: Vec<&str> = "one two three".split_whitespace().collect();
println!("{:?}", numbers);
println!("{:?}", chars);
println!("{:?}", words);
[1, 2, 3] ['a', 'b', 'c'] ["one", "two", "three"]
fn main() { let numbers: Vec<&u32> = [1, 2, 3].iter().collect(); let chars: Vec= "abc".chars().collect(); let words: Vec<&str> = "one two three".split_whitespace().collect(); println!("{:?}", numbers); println!("{:?}", chars); println!("{:?}", words); }
Итерация
let chars = String::from("abc").chars();
println!("{:?}", chars); // ???
Итерация
let chars = String::from("abc").chars();
println!("{:?}", chars);
error[E0716]: temporary value dropped while borrowed --> src/bin/main_fdc3411ae2754f8f369a49979e61218b7638899c.rs:2:13 | 2 | let chars = String::from("abc").chars(); | ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | creates a temporary which is freed while still in use 3 | 4 | println!("{:?}", chars); | ----- borrow later used here | help: consider using a `let` binding to create a longer lived value | 2 + let binding = String::from("abc"); 3 ~ let chars = binding.chars(); | For more information about this error, try `rustc --explain E0716`. error: could not compile `rust` due to previous error
fn main() { let chars = String::from("abc").chars(); println!("{:?}", chars); }
Итерация
let string = String::from("abc");
let chars = string.chars();
println!("{:?}", chars);
Chars(['a', 'b', 'c'])
fn main() { let string = String::from("abc"); let chars = string.chars(); println!("{:?}", chars); }
Итерация
let string = String::from("abc");
let mut chars = string.chars(); // Mutable!
println!("{:?}", chars.next());
println!("{:?}", chars.next());
println!("{:?}", chars.next());
println!("{:?}", chars.next());
Some('a') Some('b') Some('c') None
fn main() { let string = String::from("abc"); let mut chars = string.chars(); // Mutable! println!("{:?}", chars.next()); println!("{:?}", chars.next()); println!("{:?}", chars.next()); println!("{:?}", chars.next()); }
Итерация
let string = String::from("abc");
let mut chars = string.chars(); // Mutable!
while let Some(c) = chars.next() {
println!("{:?}", c);
}
'a' 'b' 'c'
fn main() { let string = String::from("abc"); let mut chars = string.chars(); // Mutable! while let Some(c) = chars.next() { println!("{:?}", c); } }
Итерация
let string = String::from("abc");
let mut chars = string.chars();
for c in chars {
println!("{:?}", c);
}
Итерация
let string = String::from("abc");
let mut chars = string.chars();
for c in chars {
println!("{:?}", c);
}
'a' 'b' 'c'
fn main() { let string = String::from("abc"); let mut chars = string.chars(); for c in chars { println!("{:?}", c); } }
Итерация
let string = String::from("abc");
let chars = string.chars(); // Not Mutable!
for c in chars {
println!("{:?}", c);
}
'a' 'b' 'c'
fn main() { let string = String::from("abc"); let chars = string.chars(); // Not Mutable! for c in chars { println!("{:?}", c); } }
Итерация
let string = String::from("abc");
let chars = string.chars(); // Not Mutable!
{
// (sneakily add mutability)
let mut chars = chars;
for c in chars {
println!("{:?}", c);
}
}
'a' 'b' 'c'
fn main() { let string = String::from("abc"); let chars = string.chars(); // Not Mutable! { // (sneakily add mutability) let mut chars = chars; for c in chars { println!("{:?}", c); } } }
More control flow
All together now:
let counts = [1, 2, 3, 4];
let mut counter = counts.iter();
if let Some(n) = counter.next() {
print!("{}", n);
while let Some(n) = counter.next() {
print!(" and {}", n);
}
println!();
}
More control flow
All together now:
let counts = [1, 2, 3, 4];
let mut counter = counts.iter();
if let Some(n) = counter.next() {
print!("{}", n);
while let Some(n) = counter.next() {
print!(" and {}", n);
}
println!();
}
1 and 2 and 3 and 4
fn main() { let counts = [1, 2, 3, 4]; let mut counter = counts.iter(); if let Some(n) = counter.next() { print!("{}", n); while let Some(n) = counter.next() { print!(" and {}", n); } println!(); } }
Pattern Matching
Guards (допълнителни условия)
let pair = (2, -2);
match pair {
(x, y) if x == y => println!("Едно и също"),
(x, y) if x + y == 0 => println!("Противоположни"),
(x, y) if x % 2 == 1 && y % 2 == 0 => println!("X е нечетно, Y е четно"),
(x, _) if x % 2 == 1 => println!("X е нечетно"),
_ => println!("Нищо интересно"),
}
Pattern Matching
Ranges
let age: i32 = -5;
match age {
n if n < 0 => println!("Ще се родя след {} години.", n.abs()),
0 => println!("Новородено съм."),
1 ..= 12 => println!("Аз съм лапе."),
13 ..= 19 => println!("Аз съм тийн."),
_ => println!("Аз съм дърт."),
}
1 ..= 12
означава "от 1 до 12 включително"- Можете да използвате и
...
вместо..=
, но е deprecated
Pattern Matching
Bindings
let age: i32 = -5;
match age {
n if n < 0 => println!("Ще се родя след {} години.", n.abs()),
0 => println!("Новородено съм."),
n @ 1 ..= 12 => println!("Аз съм лапе на {}.", n),
n @ 13 ..= 19 => println!("Аз съм тийн на {}.", n),
n => println!("Аз съм дърт, на {} съм вече.", n),
}
Pattern Matching
Multiple patterns
let score: u32 = 5;
match score {
0 | 1 => println!("слабичко :("),
_ => println!("стаа"),
}
Pattern Matching
Structs
struct User {
name: &'static str,
age: u8
}
let user = User {
name: "Пешо",
age: 12
};
match user {
User { name: "Пешо", age: _ } => println!("Ко стаа, Пешо"),
User { name: _, age: 12 } => println!("Ко стаа, лапе"),
User { name: x, .. } => println!("Ко стаа, {}", x),
_ => println!("Ко стаа")
}
Pattern Matching
Slice
let cake: &[&str] = &["vanilla", "strawberry", "chocolate"];
match cake {
[] => println!("Turns out it's a lie :/"),
[one_item] => println!("One slice is better than nothing"),
_ => println!("Wow, that's a lotta slices!")
}
Wow, that's a lotta slices!
fn main() { let cake: &[&str] = &["vanilla", "strawberry", "chocolate"]; match cake { [] => println!("Turns out it's a lie :/"), [one_item] => println!("One slice is better than nothing"), _ => println!("Wow, that's a lotta slices!") } }
Нищо по-fancy засега… очакват се по-интересни неща по-нататък.
Pattern Matching
Slice
Защо ни трябва експлицитен &[&str]
let cake = &["vanilla", "strawberry", "chocolate"];
// => &[&str; 3]
match cake {
[] => println!("Turns out it's a lie :/"),
[one_item] => println!("One slice is better than nothing"),
_ => println!("Wow, that's a lotta slices!")
}
error[E0527]: pattern requires 0 elements but array has 3 --> src/bin/main_7fcc5d4a5bf3f85fa23c4492b0dcf73948710ca7.rs:6:5 | 6 | [] => println!("Turns out it's a lie :/"), | ^^ expected 3 elements error[E0527]: pattern requires 1 element but array has 3 --> src/bin/main_7fcc5d4a5bf3f85fa23c4492b0dcf73948710ca7.rs:7:5 | 7 | [one_item] => println!("One slice is better than nothing"), | ^^^^^^^^^^ expected 3 elements For more information about this error, try `rustc --explain E0527`. error: could not compile `rust` due to 2 previous errors
fn main() { let cake = &["vanilla", "strawberry", "chocolate"]; // => &[&str; 3] match cake { [] => println!("Turns out it's a lie :/"), [one_item] => println!("One slice is better than nothing"), _ => println!("Wow, that's a lotta slices!") } }
Pattern Matching
Unambiguous
let numbers = (2, 4, 8, 16, 32);
match numbers {
(.., second, ..) => {
println!("Some numbers: {}", second)
},
}
error: `..` can only be used once per tuple pattern --> src/bin/main_0785486a8cc5ddcdb61694d420d4508a1c40d033.rs:5:18 | 5 | (.., second, ..) => { | -- ^^ can only be used once per tuple pattern | | | previously used here error: could not compile `rust` due to previous error
fn main() { let numbers = (2, 4, 8, 16, 32); match numbers { (.., second, ..) => { println!("Some numbers: {}", second) }, } }
Destructuring
#[derive(Debug)]
enum Token { Text(String), Number(f64) }
fn main() {
let token = Token::Text(String::from("Отговора е 42"));
match token {
Token::Text(text) => println!("Токена е текст: '{}'", text),
Token::Number(n) => println!("Токена е число: {}", n),
}
}
Токена е текст: 'Отговора е 42'
#[derive(Debug)] enum Token { Text(String), Number(f64) } fn main() { let token = Token::Text(String::from("Отговора е 42")); match token { Token::Text(text) => println!("Токена е текст: '{}'", text), Token::Number(n) => println!("Токена е число: {}", n), } }
Destructuring
#[derive(Debug)]
enum Token { Text(String), Number(f64) }
fn main() {
let token = Token::Text(String::from("Отговора е 42"));
match token {
Token::Text(text) => println!("Токена е текст: '{}'", text),
Token::Number(n) => println!("Токена е число: {}", n),
}
println!("В крайна сметка, токена е {:?}!", token);
}
error[E0382]: borrow of partially moved value: `token` --> src/bin/main_7de83c4ece9c335fd8a37b17110e37b3955439df.rs:11:49 | 7 | Token::Text(text) => println!("Токена е текст: '{}'", text), | ---- value partially moved here ... 11 | println!("В крайна сметка, токена е {:?}!", token); | ^^^^^ value borrowed here after partial move | = note: partial move occurs because value has type `String`, which does not implement the `Copy` trait = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: borrow this field in the pattern to avoid moving `token.0` | 7 | Token::Text(ref text) => println!("Токена е текст: '{}'", text), | +++ For more information about this error, try `rustc --explain E0382`. error: could not compile `rust` due to previous error
#[derive(Debug)] enum Token { Text(String), Number(f64) } fn main() { let token = Token::Text(String::from("Отговора е 42")); match token { Token::Text(text) => println!("Токена е текст: '{}'", text), Token::Number(n) => println!("Токена е число: {}", n), } println!("В крайна сметка, токена е {:?}!", token); }
Destructuring
match token
-> match &token
#[derive(Debug)]
enum Token { Text(String), Number(f64) }
fn main() {
let token = Token::Text(String::from("Отговора е 42"));
match &token {
Token::Text(text) => println!("Токена е текст: '{}'", text),
Token::Number(n) => println!("Токена е число: {}", n),
}
println!("В крайна сметка, токена е {:?}!", token);
}
Токена е текст: 'Отговора е 42' В крайна сметка, токена е Text("Отговора е 42")!
#[derive(Debug)] enum Token { Text(String), Number(f64) } fn main() { let token = Token::Text(String::from("Отговора е 42")); match &token { Token::Text(text) => println!("Токена е текст: '{}'", text), Token::Number(n) => println!("Токена е число: {}", n), } println!("В крайна сметка, токена е {:?}!", token); }
Destructuring
Друг вариант: ref
Чрез ref
стойността няма да се премести.
#[derive(Debug)]
enum Token { Text(String), Number(f64) }
fn main() {
let token = Token::Text(String::from("Отговора е 42"));
match token {
Token::Text(ref text) => println!("Токена е текст: '{}'", text),
Token::Number(n) => println!("Токена е число: {}", n),
}
println!("В крайна сметка, токена е {:?}!", token);
}
Токена е текст: 'Отговора е 42' В крайна сметка, токена е Text("Отговора е 42")!
#[allow(dead_code)] #[derive(Debug)] enum Token { Text(String), Number(f64) } fn main() { let token = Token::Text(String::from("Отговора е 42")); match token { Token::Text(ref text) => println!("Токена е текст: '{}'", text), Token::Number(n) => println!("Токена е число: {}", n), } println!("В крайна сметка, токена е {:?}!", token); }
Destructuring
ref
Какво всъщност прави ref
? Едно просто обяснение е чрез пример:
let x = &1_u32;
let ref y = 1_u32;
Destructuring
ref
Какво всъщност прави ref
? Едно просто обяснение е чрез пример:
let x = &1_u32;
let ref y = 1_u32;
- Двете променливи имат един и същ тип
Destructuring
ref
Какво всъщност прави ref
? Едно просто обяснение е чрез пример:
let x = &1_u32;
let ref y = 1_u32;
- Двете променливи имат един и същ тип
- Първият пример взима референция с
&
в дясната страна на твърдението
Destructuring
ref
Какво всъщност прави ref
? Едно просто обяснение е чрез пример:
let x = &1_u32;
let ref y = 1_u32;
- Двете променливи имат един и същ тип
- Първият пример взима референция с
&
в дясната страна на твърдението - Вторият пример взима референция с
ref
в лявата страна на твърдението
Destructuring
ref
Какво всъщност прави ref
? Едно просто обяснение е чрез пример:
let x = &1_u32;
let ref y = 1_u32;
- Двете променливи имат един и същ тип
- Първият пример взима референция с
&
в дясната страна на твърдението - Вторият пример взима референция с
ref
в лявата страна на твърдението - Това е важно, защото в
match
ръкава нямаме достъп до "дясната страна"
Destructuring
- Използвайте
match foo
ако искате да вземете ownership над вътрешните стойности
Destructuring
- Използвайте
match foo
ако искате да вземете ownership над вътрешните стойности - Използвайте
match &foo
ако искате да достъпвате вътрешностите по reference
Destructuring
- Използвайте
match foo
ако искате да вземете ownership над вътрешните стойности - Използвайте
match &foo
ако искате да достъпвате вътрешностите по reference - (Basically, изчакайте компилатора да се оплаче и добавете
&
:))
Destructuring
- Използвайте
match foo
ако искате да вземете ownership над вътрешните стойности - Използвайте
match &foo
ако искате да достъпвате вътрешностите по reference - (Basically, изчакайте компилатора да се оплаче и добавете
&
:)) - Знайте за
ref
и какво прави
Destructuring
&mut
Същата идея:
let mut token = Token::Text(String::from("Отговора е 42"));
match &mut token {
Token::Text(text) => {
*text = String::from("Може би");
println!("Токена е Text('{}')", text)
},
Token::Number(n) => println!("Токена е Number({})", n),
}
Токена е Text('Може би')
#[allow(dead_code)] #[derive(Debug)] enum Token { Text(String), Number(f64) } fn main() { let mut token = Token::Text(String::from("Отговора е 42")); match &mut token { Token::Text(text) => { *text = String::from("Може би"); println!("Токена е Text('{}')", text) }, Token::Number(n) => println!("Токена е Number({})", n), } }
Destructuring
ref mut
Същата идея:
let mut token = Token::Text(String::from("Отговора е 42"));
match token {
Token::Text(ref mut text) => {
*text = String::from("Може би");
println!("Токена е Text('{}')", text)
},
Token::Number(n) => println!("Токена е Number({})", n),
}
Токена е Text('Може би')
#[allow(dead_code)] #[derive(Debug)] enum Token { Text(String), Number(f64) } fn main() { let mut token = Token::Text(String::from("Отговора е 42")); match token { Token::Text(ref mut text) => { *text = String::from("Може би"); println!("Токена е Text('{}')", text) }, Token::Number(n) => println!("Токена е Number({})", n), } }
Destructuring
let
let (mut a, b) = (1, 2);
let User { name: name_var, .. } = user;
let User { name, .. } = user;
let Some(val) = Some(5); // ??
Destructuring
let
let (mut a, b) = (1, 2);
// let User { name: name, .. } = user;
// let User { name, .. } = user;
let Some(val) = Some(5); // ??
error[E0005]: refutable pattern in local binding: `None` not covered --> src/bin/main_d1511059f296e51051e460e98c50c64b6a469db5.rs:5:5 | 5 | let Some(val) = Some(5); // ?? | ^^^^^^^^^ pattern `None` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `Option<i32>` defined here --> /home/andrew/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:522:5 | 518 | pub enum Option<T> { | ------------------ ... 522 | None, | ^^^^ not covered = note: the matched value is of type `Option<i32>` help: you might want to use `if let` to ignore the variant that isn't matched | 5 | let val = if let Some(val) = Some(5) { val } else { todo!() }; // ?? | ++++++++++++ ++++++++++++++++++++++++ For more information about this error, try `rustc --explain E0005`. error: could not compile `rust` due to previous error
fn main() { let (mut a, b) = (1, 2); // let User { name: name, .. } = user; // let User { name, .. } = user; let Some(val) = Some(5); // ?? }
Refutable/Irrefutable patterns
let (a, b) = (1, 2); // -> Irrefutable pattern
if let Some(val) = Some(5) { // -> Refutable pattern
println!("Okay!");
}
Okay!
#[allow(unused_variables)] fn main() { let (a, b) = (1, 2); // -> Irrefutable pattern if let Some(val) = Some(5) { // -> Refutable pattern println!("Okay!"); } }
Refutable/Irrefutable patterns
if let (a, b) = (1, 2) {
println!("Nope!");
}
let Some(val) = Some(5);
warning: irrefutable `if let` pattern --> src/bin/main_a7d8ae0df38a9baad785d12585ab8eec8e5c4c94.rs:3:4 | 3 | if let (a, b) = (1, 2) { | ^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(irrefutable_let_patterns)]` on by default = note: this pattern will always match, so the `if let` is useless = help: consider replacing the `if let` with a `let` error[E0005]: refutable pattern in local binding: `None` not covered --> src/bin/main_a7d8ae0df38a9baad785d12585ab8eec8e5c4c94.rs:7:5 | 7 | let Some(val) = Some(5); | ^^^^^^^^^ pattern `None` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `Option<i32>` defined here --> /home/andrew/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:522:5 | 518 | pub enum Option<T> { | ------------------ ... 522 | None, | ^^^^ not covered = note: the matched value is of type `Option<i32>` help: you might want to use `if let` to ignore the variant that isn't matched | 7 | let val = if let Some(val) = Some(5) { val } else { todo!() }; | ++++++++++++ ++++++++++++++++++++++++ For more information about this error, try `rustc --explain E0005`. error: could not compile `rust` due to previous error; 1 warning emitted
#[allow(unused_variables)] fn main() { if let (a, b) = (1, 2) { println!("Nope!"); } let Some(val) = Some(5); }
Refutable/Irrefutable patterns
if let (1, b) = (1, 2) {
println!("{}", b);
}
if let (8, b) = (1, 2) {
println!("{}", b);
} else {
println!("8(");
}
2 8(
#[allow(unused_variables)] fn main() { if let (1, b) = (1, 2) { println!("{}", b); } if let (8, b) = (1, 2) { println!("{}", b); } else { println!("8("); } }
Refutable/Irrefutable patterns
???
if let x = true {
println!("Yup! x = {:?}", x)
}
if let x = false {
println!("Also yup! x = {:?}", x)
}
Refutable/Irrefutable patterns
big if true
if let x = true {
println!("Yup! x = {:?}", x)
}
if let x = false {
println!("Also yup! x = {:?}", x)
}
Yup! x = true Also yup! x = false
#[allow(unused_variables)] #[allow(irrefutable_let_patterns)] fn main() { if let x = true { println!("Yup! x = {:?}", x) } if let x = false { println!("Also yup! x = {:?}", x) } }
Refutable/Irrefutable patterns
big if true
if let x = true {
println!("Yup! x = {:?}", x)
}
if let x = false {
println!("Also yup! x = {:?}", x)
}
warning: irrefutable `if let` pattern --> src/bin/main_3464eb22dfe37dec3f5434c2be941b286610f0e2.rs:4:4 | 4 | if let x = true { | ^^^^^^^^^^^^ | = note: `#[warn(irrefutable_let_patterns)]` on by default = note: this pattern will always match, so the `if let` is useless = help: consider replacing the `if let` with a `let` warning: irrefutable `if let` pattern --> src/bin/main_3464eb22dfe37dec3f5434c2be941b286610f0e2.rs:8:4 | 8 | if let x = false { | ^^^^^^^^^^^^^ | = note: this pattern will always match, so the `if let` is useless = help: consider replacing the `if let` with a `let`
#[allow(unused_variables)] fn main() { if let x = true { println!("Yup! x = {:?}", x) } if let x = false { println!("Also yup! x = {:?}", x) } }
Някои линкове
- Rust vs Java Options Performance - https://pkolaczk.github.io/overhead-of-optional/