Structs, enums, pattern matching
Структури, изброени типове, съпоставяне на образци
15 октомври 2024
Преговор
Преговор
- Присвояване и местене; Clone и Copy
Преговор
- Присвояване и местене; Clone и Copy
- Собственост (ownership) и заемане (borrowing)
Преговор
- Присвояване и местене; Clone и Copy
- Собственост (ownership) и заемане (borrowing)
- Референции
Преговор
- Присвояване и местене; Clone и Copy
- Собственост (ownership) и заемане (borrowing)
- Референции
- референцията винаги сочи към валидна стойност
Преговор
- Присвояване и местене; Clone и Copy
- Собственост (ownership) и заемане (borrowing)
- Референции
- референцията винаги сочи към валидна стойност
- или произволен брой
&T
, или точно една&mut T
Преговор
- Присвояване и местене; Clone и Copy
- Собственост (ownership) и заемане (borrowing)
- Референции
- референцията винаги сочи към валидна стойност
- или произволен брой
&T
, или точно една&mut T
- Низове (
String
) и резени от низове (&str
)
Преговор
- Присвояване и местене; Clone и Copy
- Собственост (ownership) и заемане (borrowing)
- Референции
- референцията винаги сочи към валидна стойност
- или произволен брой
&T
, или точно една&mut T
- Низове (
String
) и резени от низове (&str
) - Вектори (
Vec<T>
) и резени от масиви (&[T]
)
Административни неща
- сайт на курса: https://fmi.rust-lang.bg
Административни неща
- сайт на курса: https://fmi.rust-lang.bg
- материали за упражнение: https://github.com/rust-lang/rustlings
- материали за упражнение: https://exercism.org/tracks/rust/exercises
Структури
Структури
Синтаксис
struct User {
username: String,
email: String,
sign_in_count: u64,
}
struct User { username: String, email: String, sign_in_count: u64, } fn main() {}
Структури
Създаване на инстанция
struct User {
username: String,
email: String,
sign_in_count: u64
}
fn main() {
let user = User {
username: String::from("Иванчо"),
email: String::from("ivan40@abv.bg"),
sign_in_count: 10,
};
}
struct User { username: String, email: String, sign_in_count: u64 } fn main() { let user = User { username: String::from("Иванчо"), email: String::from("ivan40@abv.bg"), sign_in_count: 10, }; }
Структури
Създаване на инстанция
Има кратък синтаксис за задаване на поле чрез променлива със същото име.
struct User { /* ... */ }
let username = String::from("Иванчо");
let email = String::from("ivan40@abv.bg");
let sign_in_count = 10;
// пълен синтаксис
let user = User {
username: username,
email: email,
sign_in_count: sign_in_count,
};
// кратък синтаксис
let user = User { username, email, sign_in_count };
Структури
Достъп до полета
struct User {
username: String,
email: String,
sign_in_count: u64
}
let user = User {
username: String::from("Иванчо"),
email: String::from("ivan40@abv.bg"),
sign_in_count: 10,
};
println!("{}, {}", user.username, user.email);
Иванчо, ivan40@abv.bg
fn main() { struct User { username: String, email: String, sign_in_count: u64 } let user = User { username: String::from("Иванчо"), email: String::from("ivan40@abv.bg"), sign_in_count: 10, }; println!("{}, {}", user.username, user.email); }
Структури
Достъп до полета
Полетата се достъпват по същия начин и през референция.
Автоматично се правят необходимия брой дереференцирания.
struct User {
username: String,
email: String,
sign_in_count: u64
}
let user = User {
username: String::from("Иванчо"),
email: String::from("ivan40@abv.bg"),
sign_in_count: 10,
};
let user_ref = &user;
println!("{}, {}", user_ref.username, user_ref.email);
Иванчо, ivan40@abv.bg
fn main() { struct User { username: String, email: String, sign_in_count: u64 } let user = User { username: String::from("Иванчо"), email: String::from("ivan40@abv.bg"), sign_in_count: 10, }; let user_ref = &user; println!("{}, {}", user_ref.username, user_ref.email); }
Структури
Промяна на полетата
Можем да променяме стойността на полетата, ако инстанцията е дефинирана като mut
.
struct User {
username: String,
email: String,
sign_in_count: u64
}
let mut user = User {
username: String::from("Иванчо"),
email: String::from("ivan40@abv.bg"),
sign_in_count: 10,
};
user.email = "ivan40.ivanov@abv.bg".to_string();
println!("{}, {}", user.username, user.email);
Иванчо, ivan40.ivanov@abv.bg
fn main() { struct User { username: String, email: String, sign_in_count: u64 } let mut user = User { username: String::from("Иванчо"), email: String::from("ivan40@abv.bg"), sign_in_count: 10, }; user.email = "ivan40.ivanov@abv.bg".to_string(); println!("{}, {}", user.username, user.email); }
Структури
Преместване на структури
По подразбиране структурите се преместват (защото не имплементират Copy
).
struct User {
username: String,
email: String,
sign_in_count: u64
}
let user1 = User {
username: String::from("Иванчо"),
email: String::from("ivan40@abv.bg"),
sign_in_count: 10,
};
let user2 = user1;
println!("user1 = {}, {}", user1.username, user1.email);
println!("user2 = {}, {}", user2.username, user2.email);
error[E0382]: borrow of moved value: `user1` --> src/bin/main_b3c03fba678cada8014f8bd9534eee85648a700d.rs:16:44 | 8 | let user1 = User { | ----- move occurs because `user1` has type `User`, which does not implement the `Copy` trait ... 14 | let user2 = user1; | ----- value moved here 15 | 16 | println!("user1 = {}, {}", user1.username, user1.email); | ^^^^^^^^^^^ value borrowed here after move | note: if `User` implemented `Clone`, you could clone the value --> src/bin/main_b3c03fba678cada8014f8bd9534eee85648a700d.rs:2:1 | 2 | struct User { | ^^^^^^^^^^^ consider implementing `Clone` for this type ... 14 | let user2 = user1; | ----- you could clone this value = 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) For more information about this error, try `rustc --explain E0382`. error: could not compile `rust` (bin "main_b3c03fba678cada8014f8bd9534eee85648a700d") due to 1 previous error
fn main() { struct User { username: String, email: String, sign_in_count: u64 } let user1 = User { username: String::from("Иванчо"), email: String::from("ivan40@abv.bg"), sign_in_count: 10, }; let user2 = user1; println!("user1 = {}, {}", user1.username, user1.email); println!("user2 = {}, {}", user2.username, user2.email); }
Структури
Клониране на структури
За да можем да създаваме копия на нашата структура, тя трябва да имплементира trait-а Clone
.
Чрез атрибута #[derive(Clone)]
компилатора автоматично ще ни създаде имплементация на Clone
.
Аналогично #[derive(Copy)]
ще имплементира Copy
- но трябва всички полета да са Copy
.
#[derive(Clone)]
struct User {
username: String,
email: String,
sign_in_count: u64
}
let user1 = User {
username: String::from("Иванчо"),
email: String::from("ivan40@abv.bg"),
sign_in_count: 10,
};
let user2 = user1.clone();
println!("user1 = {}, {}", user1.username, user1.email);
println!("user2 = {}, {}", user2.username, user2.email);
user1 = Иванчо, ivan40@abv.bg user2 = Иванчо, ivan40@abv.bg
fn main() { #[derive(Clone)] struct User { username: String, email: String, sign_in_count: u64 } let user1 = User { username: String::from("Иванчо"), email: String::from("ivan40@abv.bg"), sign_in_count: 10, }; let user2 = user1.clone(); println!("user1 = {}, {}", user1.username, user1.email); println!("user2 = {}, {}", user2.username, user2.email); }
Структури
Принтиране на структури
Аналогично можем да използваме атрибута #[derive(Debug)]
за да получим имплементация на trait-а Debug
.
Това ни позволява да принтираме нашата структура с println!
инползвайки placeholder {:?}
.
#[derive(Clone, Debug)]
struct User {
username: String,
email: String,
sign_in_count: u64
}
let user1 = User {
username: String::from("Иванчо"),
email: String::from("ivan40@abv.bg"),
sign_in_count: 10,
};
let user2 = user1.clone();
println!("user1 = {:?}", user1);
println!("user2 = {:?}", user2);
user1 = User { username: "Иванчо", email: "ivan40@abv.bg", sign_in_count: 10 } user2 = User { username: "Иванчо", email: "ivan40@abv.bg", sign_in_count: 10 }
fn main() { #[derive(Clone, Debug)] struct User { username: String, email: String, sign_in_count: u64 } let user1 = User { username: String::from("Иванчо"), email: String::from("ivan40@abv.bg"), sign_in_count: 10, }; let user2 = user1.clone(); println!("user1 = {:?}", user1); println!("user2 = {:?}", user2); }
Структури
Struct update синтаксис
Можем да дадем стойност само на част от полетата и останалите да попълним от друга инстанция
let user = User {
username: String::from("Иванчо"),
email: String::from("ivan40@abv.bg"),
sign_in_count: 10,
};
let hacker = User {
email: String::from("hackerman@l33t.hax"),
..user
};
println!("{:?}", hacker);
User { username: "Иванчо", email: "hackerman@l33t.hax", sign_in_count: 10 }
#[derive(Debug)] struct User { username: String, email: String, sign_in_count: u64 } fn main() { let user = User { username: String::from("Иванчо"), email: String::from("ivan40@abv.bg"), sign_in_count: 10, }; let hacker = User { email: String::from("hackerman@l33t.hax"), ..user }; println!("{:?}", hacker); }
Структури
Struct update синтаксис
Това ще премести полетата от оригиналната инстанция
let user = User {
username: String::from("Иванчо"),
email: String::from("ivan40@abv.bg"),
sign_in_count: 10,
};
let hacker = User {
email: String::from("hackerman@l33t.hax"),
..user
};
println!("{:?}", hacker);
println!("{:?}", user);
error[E0382]: borrow of partially moved value: `user` --> src/bin/main_99773612f004f2399763eb71a56006b2f8c4c270.rs:16:18 | 10 | let hacker = User { | ______________- 11 | | email: String::from("hackerman@l33t.hax"), 12 | | ..user 13 | | }; | |_- value partially moved here ... 16 | println!("{:?}", user); | ^^^^ value borrowed here after partial move | = note: partial move occurs because `user.username` 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) For more information about this error, try `rustc --explain E0382`. error: could not compile `rust` (bin "main_99773612f004f2399763eb71a56006b2f8c4c270") due to 1 previous error
#[derive(Debug)] struct User { username: String, email: String, sign_in_count: u64 } fn main() { let user = User { username: String::from("Иванчо"), email: String::from("ivan40@abv.bg"), sign_in_count: 10, }; let hacker = User { email: String::from("hackerman@l33t.hax"), ..user }; println!("{:?}", hacker); println!("{:?}", user); }
Структури
Struct update синтаксис
Синтаксиса е удобен за попълване на стойности по подразбиране.
Напр. ако структурата имплементира трейта Default
можем да използваме функцията default
.
let user = User {
username: String::from("Иванчо"),
..User::default()
};
println!("{:?}", user);
User { username: "Иванчо", email: "", sign_in_count: 0 }
#[derive(Debug, Default)] struct User { username: String, email: String, sign_in_count: u64 } fn main() { let user = User { username: String::from("Иванчо"), ..User::default() }; println!("{:?}", user); }
Методи и асоциирани функции
Асоциирани функции
Методи и асоциирани функции
Асоциирани функции
struct User { ... }
impl User {
fn new(username: String, email: String) -> User {
User {
username: username,
email: email,
sign_in_count: 0,
}
}
}
fn main() {} struct User { username: String, email: String, sign_in_count: u64 } /* struct User { ... } */ impl User { fn new(username: String, email: String) -> User { User { username: username, email: email, sign_in_count: 0, } } }
Методи и асоциирани функции
Асоциирани функции
struct User { ... }
impl User {
fn new(username: String, email: String) -> User {
User {
username: username,
email: email,
sign_in_count: 0,
}
}
}
fn main() {} struct User { username: String, email: String, sign_in_count: u64 } /* struct User { ... } */ impl User { fn new(username: String, email: String) -> User { User { username: username, email: email, sign_in_count: 0, } } }
struct
блока съдържа само полетата на структурата
Методи и асоциирани функции
Асоциирани функции
struct User { ... }
impl User {
fn new(username: String, email: String) -> User {
User {
username: username,
email: email,
sign_in_count: 0,
}
}
}
fn main() {} struct User { username: String, email: String, sign_in_count: u64 } /* struct User { ... } */ impl User { fn new(username: String, email: String) -> User { User { username: username, email: email, sign_in_count: 0, } } }
struct
блока съдържа само полетата на структурата- методи и функции се добавят в отделен
impl
блок
Методи и асоциирани функции
Асоциирани функции
struct User { ... }
impl User {
fn new(username: String, email: String) -> User {
User {
username: username,
email: email,
sign_in_count: 0,
}
}
}
fn main() {} struct User { username: String, email: String, sign_in_count: u64 } /* struct User { ... } */ impl User { fn new(username: String, email: String) -> User { User { username: username, email: email, sign_in_count: 0, } } }
struct
блока съдържа само полетата на структурата- методи и функции се добавят в отделен
impl
блок - разделение между данни и логика
Методи и асоциирани функции
Асоциирани функции
struct User { ... }
impl User {
fn new(username: String, email: String) -> User {
User {
username: username,
email: email,
sign_in_count: 0,
}
}
}
let user = User::new(String::from("Иванчо"), String::from("ivan40@abv.bg"));
struct User { username: String, email: String, sign_in_count: u64 } /* struct User { ... } */ impl User { fn new(username: String, email: String) -> User { User { username: username, email: email, sign_in_count: 0, } } } fn main() { let user = User::new(String::from("Иванчо"), String::from("ivan40@abv.bg")); }
Методи и асоциирани функции
Конструктори и деструктори
Методи и асоциирани функции
Конструктори и деструктори
- в Rust няма конструктори
Методи и асоциирани функции
Конструктори и деструктори
- в Rust няма конструктори
- конвенция е да има асоциирана функция, която създава обект от типа
Методи и асоциирани функции
Конструктори и деструктори
- в Rust няма конструктори
- конвенция е да има асоциирана функция, която създава обект от типа
- обикновенно името е
new
from_*
with_*
Методи и асоциирани функции
Конструктори и деструктори
- в Rust няма конструктори
- конвенция е да има асоциирана функция, която създава обект от типа
- обикновенно името е
new
from_*
with_*
- но има и изключения, напр.
File::open
Методи и асоциирани функции
Конструктори и деструктори
- в Rust има деструктори
- дефинират се чрез trait-а
Drop
- за тях ще говорим по-късно
Методи и асоциирани функции
Методи
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn area(&self) -> f64 { self.width * self.height } } fn main() {}
Методи и асоциирани функции
Методи
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn area(&self) -> f64 { self.width * self.height } } fn main() {}
- функция, която приема като първи аргумент
self
,&self
,&mut self
(method receiver)
Методи и асоциирани функции
Методи
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn area(&self) -> f64 { self.width * self.height } } fn main() {}
- функция, която приема като първи аргумент
self
,&self
,&mut self
(method receiver) - полетата се достъпват през аргумента
self
Методи и асоциирани функции
Типа Self
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn new(width: f64, height: f64) -> Self {
Self { width, height }
}
}
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn new(width: f64, height: f64) -> Self { Self { width, height } } } fn main() {}
Методи и асоциирани функции
Типа Self
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn new(width: f64, height: f64) -> Self {
Self { width, height }
}
}
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn new(width: f64, height: f64) -> Self { Self { width, height } } } fn main() {}
- достъпен във вътрешността
impl
блок
Методи и асоциирани функции
Типа Self
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn new(width: f64, height: f64) -> Self {
Self { width, height }
}
}
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn new(width: f64, height: f64) -> Self { Self { width, height } } } fn main() {}
- достъпен във вътрешността
impl
блок - псевдоним на типа, за който имплементираме
Методи и асоциирани функции
Методи
self
е еквивалентно наself: Self
&self
е еквивалентно наself: &Self
&mut self
е еквивалентно наself: &mut Self
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
// е еквиваленто на
fn area(self: &Self) -> f64 {
self.width * self.height
}
// е еквиваленто на
fn area(self: &Rectangle) -> f64 {
self.width * self.height
}
}
Методи и асоциирани функции
Методи
Могат да се извикват със синтаксиса за методи
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
let rect = Rectangle::new(2.0, 3.0);
let area = rect.area();
println!("area = {}", area);
area = 6
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn new(width: f64, height: f64) -> Self { Self { width, height } } fn area(&self) -> f64 { self.width * self.height } } fn main() { let rect = Rectangle::new(2.0, 3.0); let area = rect.area(); println!("area = {}", area); }
Методи и асоциирани функции
Методи
Могат да се извикват със синтаксиса за методи
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
let rect = Rectangle::new(2.0, 3.0);
let area = rect.area();
println!("area = {}", area);
area = 6
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn new(width: f64, height: f64) -> Self { Self { width, height } } fn area(&self) -> f64 { self.width * self.height } } fn main() { let rect = Rectangle::new(2.0, 3.0); let area = rect.area(); println!("area = {}", area); }
- както полетата, методите се достъпват с
.
Методи и асоциирани функции
Методи
Могат да се извикват със синтаксиса за методи
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
let rect = Rectangle::new(2.0, 3.0);
let area = rect.area();
println!("area = {}", area);
area = 6
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn new(width: f64, height: f64) -> Self { Self { width, height } } fn area(&self) -> f64 { self.width * self.height } } fn main() { let rect = Rectangle::new(2.0, 3.0); let area = rect.area(); println!("area = {}", area); }
- както полетата, методите се достъпват с
.
- компилаторът автоматично добавя
*
,&
или&mut
, така че типа на аргумента да съвпадне с типа на method receiver-а
Методи и асоциирани функции
Методи
Могат да се извикват и като асоциирани функции
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
let rect = Rectangle::new(2.0, 3.0);
let area = Rectangle::area(&rect);
println!("area = {}", area);
area = 6
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn new(width: f64, height: f64) -> Self { Self { width, height } } fn area(&self) -> f64 { self.width * self.height } } fn main() { let rect = Rectangle::new(2.0, 3.0); let area = Rectangle::area(&rect); println!("area = {}", area); }
Методи и асоциирани функции
Множество impl блокове
Позволено е декларирането на повече от един impl
блок. Удобно е при групиране на методи.
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
impl Rectangle {
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
}
struct Rectangle { width: f64, height: f64 } impl Rectangle { fn area(&self) -> f64 { self.width * self.height } } impl Rectangle { fn perimeter(&self) -> f64 { 2.0 * (self.width + self.height) } } fn main() {}
Tuples
(преговор)
(A, B, C, ...)
let tuple: (i32, u32, bool) = (1, 2, false);
println!("{}", tuple.0);
println!("{}", tuple.1);
println!("{}", tuple.2);
1 2 false
fn main() { let tuple: (i32, u32, bool) = (1, 2, false); println!("{}", tuple.0); println!("{}", tuple.1); println!("{}", tuple.2); }
Tuple structs
Именувани кортежи
struct Color(f32, f32, f32);
struct Point(f32, f32, f32);
let black = Color(0.0, 0.0, 0.0);
let origin = Point(0.0, 0.0, 0.0);
fn main() { struct Color(f32, f32, f32); struct Point(f32, f32, f32); let black = Color(0.0, 0.0, 0.0); let origin = Point(0.0, 0.0, 0.0); }
Tuple structs
Полетата се достъпват с .0
, .1
, и т.н., както при нормален tupple
struct Color(f32, f32, f32);
let black = Color(0.0, 0.0, 0.0);
println!("r: {}, g: {}, b: {}", black.0, black.1, black.2);
r: 0, g: 0, b: 0
fn main() { struct Color(f32, f32, f32); let black = Color(0.0, 0.0, 0.0); println!("r: {}, g: {}, b: {}", black.0, black.1, black.2); }
Tuple structs
Newtype wrapper
Tuple struct с едно поле често се използва за typesafe wrapper.
Това се нарича newtype struct или newtype wrapper.
#[derive(Debug, Clone, Copy)]
struct Token(u32);
#[derive(Debug, Clone, Copy)] struct Token(u32); fn main() {}
Празни структури
Възможна е декларацията на празни структури. Могат да се използват като маркери - големината им е 0 байта.
struct Electron {}
struct Proton;
let x = Electron {};
let y = Proton;
fn main() { struct Electron {} struct Proton; let x = Electron {}; let y = Proton; }
Enums
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
- Product types
Enums
Защо?
- Повечето типове са "контейнери" -- struct, list, tuple
- Product types
struct User { username: String, email: String, sign_in_count: u64 }
// множеството от възможни стойности за `User` е декартовото произведение
// String × String × u64
Enums
Защо?
Могат да се използват за взаимно изключващи се данни
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 ви позволява да изразите "екслузивност" -- или един пакет данни, или друг
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
fn main() {} enum IpAddr { V4(u8, u8, u8, u8), V6(String), }
Enums
Защо?
- Enum ви позволява да изразите "екслузивност" -- или един пакет данни, или друг
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
fn main() {} enum IpAddr { V4(u8, u8, u8, u8), V6(String), }
- Sum type - множестовто от възможни стойности е обединение на множества
V4(u8, u8, u8, u8) ∪ V6(String)
Enums
Защо?
- при
struct
е възможно да имаме стойности, които са- валидни побитово
- невалидни логически
Enums
Защо?
- при
struct
е възможно да имаме стойности, които са- валидни побитово
- невалидни логически
- чрез
enum
можем да изразим всички валидни стойности чрез типовата система
Enums
Защо?
- при
struct
е възможно да имаме стойности, които са- валидни побитово
- невалидни логически
- чрез
enum
можем да изразим всички валидни стойности чрез типовата система - make invalid states unrepresentable
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
? Ще видим след няколко слайда :)
Разполагане в паметта
Вариант | Памет |
---|---|
Message::Quit Message::Move { x: i64, y: i64 } Message::Write(String) Message::ChangeColor(i64, i64, i64) |
+–––––––++–––––––––––––––––––––––+ │ 0 ││ │ +–––––––++–––––––+–––––––+–––––––+ │ 1 ││ i64 │ i64 │ │ +–––––––++–––––––+–––––––+–––––––+ │ 2 ││ String │ +–––––––++–––––––+–––––––+–––––––+ │ 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::()); }
C-like enum
#[derive(Debug)]
#[repr(i32)]
enum Basic {
A = 0,
B = 12,
}
fn main() {
println!("{:?}", Basic::B);
println!("{:?}", Basic::B as i32);
}
B 12
#[derive(Debug)] #[repr(i32)] enum Basic { A = 0, B = 12, } fn main() { println!("{:?}", Basic::B); println!("{:?}", Basic::B as i32); }
C-like enum
#[derive(Debug)]
#[repr(i32)]
enum Basic {
A = 0,
B = 12,
}
fn main() {
println!("{:?}", Basic::B);
println!("{:?}", Basic::B as i32);
println!("{:?}", 12 as Basic);
}
error[E0605]: non-primitive cast: `i32` as `Basic` --> src/bin/main_bc0a03e54fa3ae812422d0cb1fca065483329aaa.rs:11:22 | 11 | println!("{:?}", 12 as Basic); | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object For more information about this error, try `rustc --explain E0605`. error: could not compile `rust` (bin "main_bc0a03e54fa3ae812422d0cb1fca065483329aaa") due to 1 previous error
#[derive(Debug)] #[repr(i32)] enum Basic { A = 0, B = 12, } fn main() { println!("{:?}", Basic::B); println!("{:?}", Basic::B as i32); println!("{:?}", 12 as Basic); }
C-like enum
#[derive(Debug)]
#[repr(i32)]
enum Basic {
A = 0,
B = 12,
}
impl Basic {
fn from_i32(i: i32) -> Basic {
match i {
0 => Basic::A,
12 => Basic::B,
_ => panic!("грешка!"),
}
}
}
fn main() {
println!("{:?}", Basic::from_i32(12));
}
B
#[derive(Debug)] #[repr(i32)] enum Basic { A = 0, B = 12, } impl Basic { fn from_i32(i: i32) -> Basic { match i { 0 => Basic::A, 12 => Basic::B, _ => panic!("грешка!"), } } } fn main() { println!("{:?}", Basic::from_i32(12)); }
Non-zero оптимизация
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:: ()); }
Non-zero оптимизация
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:: ()); }
String
има поле (ptr
), което не може да е нула/null- компилатора го използва като слот, където да сложи другия вариант (
LeanAndMean::A
)
Non-zero оптимизация
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: 24
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
enum Option<T> {
Some(T),
None,
}
fn main() {} enum Option{ Some(T), 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 --> /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/option.rs:571:1 ::: /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/option.rs:575:5 | = note: 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` (bin "main_95c8682b7401bcccd982dc5ffc0ed7d72379cdae") due to 1 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"¯\_(ツ)_/¯"),
}
fn main() { let x = 3; 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` (bin "main_48ccf2f6abbea81f52e3d53a72c573d98dd05a43") due to 1 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();
// `next()` метода на итератора връща `Option`
while let Some(8) = iter8or.next() {
println!("∞");
}
∞ ∞ ∞
fn main() { let so_eighty = [8, 8, 8, 88, 8]; let mut iter8or = so_eighty.iter(); // `next()` метода на итератора връща `Option` while let Some(8) = iter8or.next() { 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!("Нищо интересно"),
}
fn main() { 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!("Аз съм дърт."),
}
fn main() { 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` (bin "main_7fcc5d4a5bf3f85fa23c4492b0dcf73948710ca7") 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` (bin "main_0785486a8cc5ddcdb61694d420d4508a1c40d033") due to 1 previous error
fn main() { let numbers = (2, 4, 8, 16, 32); match numbers { (.., second, ..) => { println!("Some numbers: {}", second) }, } }
Destructuring
На аргументи
users_map.iter().for_each(print_user_1);
users_map.iter().for_each(print_user_2);
fn print_user_1(pair: (&i32, &String)) {
println!("{} => {}", pair.0, pair.1);
}
fn print_user_2((id, name): (&i32, &String)) {
println!("{} => {}", id, name);
}
1 => Иванчо 5 => Петко 1 => Иванчо 5 => Петко
fn main() { let mut users_map = std::collections::BTreeMap::new(); users_map.insert(1, "Иванчо".to_string()); users_map.insert(5, "Петко".to_string()); users_map.iter().for_each(print_user_1); users_map.iter().for_each(print_user_2); fn print_user_1(pair: (&i32, &String)) { println!("{} => {}", pair.0, pair.1); } fn print_user_2((id, name): (&i32, &String)) { println!("{} => {}", id, name); } }
Destructuring
На аргументи
users_map.iter().for_each(|pair| {
println!("{} => {}", pair.0, pair.1)
});
users_map.iter().for_each(|(id, name)| {
println!("{} => {}", id, name)
});
1 => Иванчо 5 => Петко 1 => Иванчо 5 => Петко
fn main() { let mut users_map = std::collections::BTreeMap::new(); users_map.insert(1, "Иванчо".to_string()); users_map.insert(5, "Петко".to_string()); users_map.iter().for_each(|pair| { println!("{} => {}", pair.0, pair.1) }); users_map.iter().for_each(|(id, name)| { println!("{} => {}", id, name) }); }
Destructuring
На let bindings
let (a, b, c) = (1, true, "three");
let (a, ..) = (1, true, "three");
let (.., c) = (1, true, "three");
let [a, b, c] = [1, 2, 3];
let [a, ..] = [1, 2, 3];
let [.., c] = [1, 2, 3];
fn main() { let (a, b, c) = (1, true, "three"); let (a, ..) = (1, true, "three"); let (.., c) = (1, true, "three"); let [a, b, c] = [1, 2, 3]; let [a, ..] = [1, 2, 3]; let [.., c] = [1, 2, 3]; }
Destructuring
На let bindings
let User { name: name_var, .. } = user;
let User { name, .. } = user;
fn main() { #[derive(Default)] struct User { name: String, email: String } let user = User::default(); let User { name: name_var, .. } = user; let user = User::default(); let User { name, .. } = user; }