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 няма конструктори
- конвенция е да има асоциирана функция, която създава обект от типа
- обикновенно името е
newfrom_*with_*
Методи и асоциирани функции
Конструктори и деструктори
- в Rust няма конструктори
- конвенция е да има асоциирана функция, която създава обект от типа
- обикновенно името е
newfrom_*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;
}