Еnums, pattern matching, iteration
Енумерации, съпоставяне на образци, итерация
21 октомври 2025
Преговор
- собственост (ownership)
- семантика на преместване (move semantics)
- семантика на копиране - при примитивни типове
let s1 = String::from("foo bar");
let s2 = s1;
// println!("{}", s1); // error: use of moved value
println!("{}", s2);
foo bar
fn main() {
let s1 = String::from("foo bar");
let s2 = s1;
// println!("{}", s1); // error: use of moved value
println!("{}", s2);
}
Преговор
Преговор
- borrowing, референции
- указател, винаги сочи към валидна стойност
Преговор
- borrowing, референции
- указател, винаги сочи към валидна стойност
- Borrow checker
- или произволен брой споделени референции
&T - или точно една ексклузивна референция
&mut T
- или произволен брой споделени референции
Преговор
Резени, масиви и низове
| Масив | Низ | ||
|---|---|---|---|
| статичен | [T; N] |
- | собственост над стойността |
| динамичен | Vec<T> |
String |
собственост над стойността |
| резен | &[T] |
&str |
заета назаем стойност (borrow) |
| mutable резен | &mut [T] |
&mut str |
заета назаем стойност (borrow) |
Структури
Структури
Синтаксис
struct User {
username: String,
email: String,
sign_in_count: u64,
}
struct User {
username: String,
email: String,
sign_in_count: u64,
}
fn main() {}
Методи и асоциирани функции
Асоциирани функции
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,
}
}
}
Методи и асоциирани функции
Асоциирани функции
- фунцкия, намираща се в namespace-а на структурата
- достъпва се с
::
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@example.com"));
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@example.com"));
}
Методи и асоциирани функции
Конструктори и деструктори
Методи и асоциирани функции
Конструктори и деструктори
- в Rust няма конструктори
Методи и асоциирани функции
Конструктори и деструктори
- в Rust няма конструктори
- конвенция е да има асоциирана функция, която създава инстанция от типа
Методи и асоциирани функции
Конструктори и деструктори
- в Rust няма конструктори
- конвенция е да има асоциирана функция, която създава инстанция от типа
- обикновенно името е
newfrom_*with_*
Методи и асоциирани функции
Конструктори и деструктори
- в Rust няма конструктори
- конвенция е да има асоциирана функция, която създава инстанция от типа
- обикновенно името е
newfrom_*with_*
- но има и изключения, напр.
File::open
Методи и асоциирани функции
Конструктори и деструктори
Методи и асоциирани функции
Конструктори и деструктори
- в Rust има деструктори
Методи и асоциирани функции
Конструктори и деструктори
- в Rust има деструктори
- дефинират се чрез trait-а
Drop
Методи и асоциирани функции
Конструктори и деструктори
- в Rust има деструктори
- дефинират се чрез trait-а
Drop - за тях ще говорим по-късно
Методи и асоциирани функции
Методи
- функция, която приема като първи аргумент
self,&self,&mut self(method receiver) - полетата се достъпват през аргумента
self
struct User { /* ... */ }
impl User {
fn check_and_sign_in(&mut self, email: &str) -> bool {
if self.email == email {
self.sign_in_count += 1;
true
} else {
false
}
}
}
struct User { username: String, email: String, sign_in_count: u32 }
/*
struct User { /* ... */ }
*/
impl User {
fn check_and_sign_in(&mut self, email: &str) -> bool {
if self.email == email {
self.sign_in_count += 1;
true
} else {
false
}
}
}
fn main() {}
Методи и асоциирани функции
Методи
selfе еквивалентно наself: Self&selfе еквивалентно наself: &Self&mut selfе еквивалентно наself: &mut Self
Методи и асоциирани функции
Типа Self
Selfе псевдоним (alias) на типа, за който имплементираме- достъпен е във вътрешността на
implблока
struct Rectangle { width: f64, height: f64 }
impl Rectangle {
fn new(width: f64, height: f64) -> Self {
Self { width, height }
}
// еквивалентно на
fn new(width: f64, height: f64) -> Rectangle {
Rectangle { width, height }
}
}
Методи и асоциирани функции
Методи
- както полетата, методите се достъпват с
. - компилаторът автоматично добавя
*,&или&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 = 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 = 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() {}
Структури
Ниво на достъп
По подразбиране, всички типове, полета, функции и т.н. са private
Структури
Ниво на достъп
По подразбиране, всички типове, полета, функции и т.н. са private
В Rust
- private означава, че може да се достъпва само от същия модул, в който нещото е дефинирано, или от негови подмодули
Структури
Ниво на достъп
По подразбиране, всички типове, полета, функции и т.н. са private
В Rust
- private означава, че може да се достъпва само от същия модул, в който нещото е дефинирано, или от негови подмодули
- public означава, че може да се достъпва извън този модул
Структури
Ниво на достъп
По подразбиране, всички типове, полета, функции и т.н. са private
В Rust
- private означава, че може да се достъпва само от същия модул, в който нещото е дефинирано, или от негови подмодули
- public означава, че може да се достъпва извън този модул
Eдин модул обикновенно съответства на един файл
Структури
Ниво на достъп
По подразбиране, всички типове, полета, функции и т.н. са private
В Rust
- private означава, че може да се достъпва само от същия модул, в който нещото е дефинирано, или от негови подмодули
- public означава, че може да се достъпва извън този модул
Eдин модул обикновенно съответства на един файл
- досега сме работили само с главния модул (
main.rs/lib.rs)
Структури
Ниво на достъп
За да напраивм нещо публично достъпно, трябва да използвам ключовата дума pub
pub struct User {
pub username: String,
pub email: String,
sign_in_count: u32,
}
impl User {
pub fn new(username: String, email: String) -> User {
User { username, email, sign_in_count: 0 }
}
pub fn check_and_sign_in(&mut self) -> bool {
/* ... */
}
}
pub struct User {
pub username: String,
pub email: String,
sign_in_count: u32,
}
impl User {
pub fn new(username: String, email: String) -> User {
User { username, email, sign_in_count: 0 }
}
pub fn check_and_sign_in(&mut self) -> bool {
/* ... */
todo!()
}
}
fn main() {}
Структури
getters and setters
- ако типа ни поддържа някаква инварианта, полетата би следвало да са private
Структури
getters and setters
- ако типа ни поддържа някаква инварианта, полетата би следвало да са private
- в такъв случай ще са ни нужни getter-и и/или setter-и
Структури
getters and setters
- ако типа ни поддържа някаква инварианта, полетата би следвало да са private
- в такъв случай ще са ни нужни getter-и и/или setter-и
- конвенцията е:
Структури
getters and setters
- ако типа ни поддържа някаква инварианта, полетата би следвало да са private
- в такъв случай ще са ни нужни getter-и и/или setter-и
- конвенцията е:
- името на getter-а съвпада с името на променливата
Структури
getters and setters
- ако типа ни поддържа някаква инварианта, полетата би следвало да са private
- в такъв случай ще са ни нужни getter-и и/или setter-и
- конвенцията е:
- името на getter-а съвпада с името на променливата
- името на setter-а има префикс
set_пред името на променливата
Структури
getters and setters
struct Rectangle {
width: f64,
height: f64,
precomputed_area: f64,
}
impl Rectangle {
pub fn width(&self) -> f64 {
self.width
}
pub fn set_width(&mut self, width: f64) {
self.width = width;
self.precomputed_area = self.height * self.width;
}
pub fn area(&self) -> f64 {
self.precomputed_area
}
}
struct Rectangle {
width: f64,
height: f64,
precomputed_area: f64,
}
impl Rectangle {
pub fn width(&self) -> f64 {
self.width
}
pub fn set_width(&mut self, width: f64) {
self.width = width;
self.precomputed_area = self.height * self.width;
}
pub fn area(&self) -> f64 {
self.precomputed_area
}
}
fn main() {}
Структури
getters and setters
Това е възможно, защото полетата и методите използват различни полета от имена
struct Rectangle { width: f64 }
impl Rectangle {
pub fn width(&self) -> f64 { self.width }
}
// полето width
rect.width
// метода width
rect.width()
Rect::width
Структури
getters and setters
Това е възможно, защото полетата и методите използват различни полета от имена
struct Rectangle { width: f64 }
impl Rectangle {
pub fn width(&self) -> f64 { self.width }
}
// полето width
rect.width
// метода width
rect.width()
Rect::width
В по-редкия случай, когато полето е от тип фунцкия, се използват допълнителни скоби
(my_struct.f)()
Структури
getters and setters
Забележка - не прекалявайте с getter-и и setter-и
Структури
getters and setters
Забележка - не прекалявайте с getter-и и setter-и
- ако нещо може да се използва като чисти данни - направете го структура с публични полета
Структури
getters and setters
Забележка - не прекалявайте с getter-и и setter-и
- ако нещо може да се използва като чисти данни - направете го структура с публични полета
- помислете има ли смисъл - типа ви има ли инварианта, която да задължава полетата да са private
Структури
getters and setters
Забележка - не прекалявайте с getter-и и setter-и
- ако нещо може да се използва като чисти данни - направете го структура с публични полета
- помислете има ли смисъл - типа ви има ли инварианта, която да задължава полетата да са private
- не използвайте getter-и само за immutability -
&MyStructе достатъчно immutable
- не използвайте getter-и само за immutability -
Структури
getters and setters
Забележка - не прекалявайте с getter-и и setter-и
- ако нещо може да се използва като чисти данни - направете го структура с публични полета
- помислете има ли смисъл - типа ви има ли инварианта, която да задължава полетата да са private
- не използвайте getter-и само за immutability -
&MyStructе достатъчно immutable - ако има и getter и setter и не правят нищо друго освен да чета и презаписват поле - това е ективаленто на публично поле
- не използвайте getter-и само за immutability -
Структури
getters and setters
Забележка - не прекалявайте с getter-и и setter-и
- ако нещо може да се използва като чисти данни - направете го структура с публични полета
- помислете има ли смисъл - типа ви има ли инварианта, която да задължава полетата да са private
- не използвайте getter-и само за immutability -
&MyStructе достатъчно immutable - ако има и getter и setter и не правят нищо друго освен да чета и презаписват поле - това е ективаленто на публично поле
- не използвайте getter-и само за immutability -
Има аргумент, че скриването на полета зад getter-и и setter-и позволява да се променя memory layout-а на структурата (да се добавят или премахват полета), без да се афектира публичния интерфейс.
Структури
getters and setters
Забележка - не прекалявайте с getter-и и setter-и
- ако нещо може да се използва като чисти данни - направете го структура с публични полета
- помислете има ли смисъл - типа ви има ли инварианта, която да задължава полетата да са private
- не използвайте getter-и само за immutability -
&MyStructе достатъчно immutable - ако има и getter и setter и не правят нищо друго освен да чета и презаписват поле - това е ективаленто на публично поле
- не използвайте getter-и само за immutability -
Има аргумент, че скриването на полета зад getter-и и setter-и позволява да се променя memory layout-а на структурата (да се добавят или премахват полета), без да се афектира публичния интерфейс.
- това сравнително рядко е фактор
Структури
getters and setters
Забележка - не прекалявайте с getter-и и setter-и
- ако нещо може да се използва като чисти данни - направете го структура с публични полета
- помислете има ли смисъл - типа ви има ли инварианта, която да задължава полетата да са private
- не използвайте getter-и само за immutability -
&MyStructе достатъчно immutable - ако има и getter и setter и не правят нищо друго освен да чета и презаписват поле - това е ективаленто на публично поле
- не използвайте getter-и само за immutability -
Има аргумент, че скриването на полета зад getter-и и setter-и позволява да се променя memory layout-а на структурата (да се добавят или премахват полета), без да се афектира публичния интерфейс.
- това сравнително рядко е фактор
- особено в език като Rust, който няма стабилно ABI
Празни структури
Възможна е декларация на структури без полета.
Големината им е 0 байта.
struct Proton {}
struct Electron;
let x = Proton {};
let y = Electron;
fn main() {
struct Proton {}
struct Electron;
let x = Proton {};
let y = Electron;
}
Tuples
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
Ниво на достъп
Полетата в tuple struct също са private по подразбиране.
За да могат да се достъпват отвън, трябва да се декларират с pub.
pub struct Color(pub f32, pub f32, pub f32);
fn main() {
pub struct Color(pub f32, pub f32, pub f32);
}
Tuple structs
Newtype wrapper
Tuple struct с едно поле често се използва за typesafe wrapper.
Това се нарича newtype.
#[derive(Debug, Clone, Copy)]
struct Token(u32);
#[derive(Debug, Clone, Copy)]
struct Token(u32);
fn main() {}
Tuple structs
Разлика между type alias и newtype
- type alias е ново име за съществуващ тип
- удобно е за преименуване на много дълги типове използващи generics
- и не за много повече
- не предоставя никаква сигурност
type Node<T> = Rc<RefCell<TreeNode<T>>>;
use std::rc::Rc; use std::cell::RefCell; struct TreeNode{ x: T } fn main() {} type Node = Rc >>;
Tuple structs
Разлика между type alias и newtype
- newtype създава изцяло нов тип, който има същото разполагане в паметта като съшествуващ тип
- сигурност на ниво типова система
- предотвратява объркване на аргументи
struct UserId(pub u32);
fn find_user(id: UserId) {}
// let id = 10;
// find_user(id); // error
let id = UserId(10);
find_user(id); // ok
fn main() {
struct UserId(pub u32);
fn find_user(id: UserId) {}
// let id = 10;
// find_user(id); // error
let id = UserId(10);
find_user(id); // ok
}
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
struct User { username: String, email: String, sign_in_count: u64 }
Enums
Защо?
- Повечето типове са "контейнери" -- struct, list, tuple
struct User { username: String, email: String, sign_in_count: u64 }
- Product types
- множеството от възможни стойности за
Userе декартовото произведение String1 × String2 × u64
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 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
Защо?
Структури могат да се използват за взаимно изключващи се данни
Не е особено ясно -- човек трябва "да се усети" като чете кода.
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 можем да изразим всички валидни стойности чрез типовата система
Enums
Защо?
Чрез 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 {
if i == 0 {
Basic::A
} else if i == 12 {
Basic::B
} else {
_ => panic!("грешка!"),
}
}
}
fn main() {
println!("{:?}", Basic::from_i32(12));
}
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `=>` --> src/bin/main_0b858b35302dd1654ab2355517db7d7cc9ee1839.rs:15:15 | 15 | _ => panic!("грешка!"), | ^^ expected one of `.`, `;`, `?`, `}`, or an operator | help: you might have meant to write a "greater than or equal to" comparison | 15 - _ => panic!("грешка!"), 15 + _ >= panic!("грешка!"), | error: could not compile `rust` (bin "main_0b858b35302dd1654ab2355517db7d7cc9ee1839") due to 1 previous error
#[derive(Debug)]
#[repr(i32)]
enum Basic {
A = 0,
B = 12,
}
impl Basic {
fn from_i32(i: i32) -> Basic {
if i == 0 {
Basic::A
} else if i == 12 {
Basic::B
} else {
_ => panic!("грешка!"),
}
}
}
fn main() {
println!("{:?}", Basic::from_i32(12));
}
Pattern Matching
"Съпоставяне на образци"
Pattern Matching
"Съпоставяне на образци"
- Идея идваща от функционалното програмиране
Pattern Matching
"Съпоставяне на образци"
- Идея идваща от функционалното програмиране
- Може да се ползва с енуми и стойности в тях
Pattern Matching
"Съпоставяне на образци"
- Идея идваща от функционалното програмиране
- Може да се ползва с енуми и стойности в тях
- Използва се чрез
matchоператора
Pattern Matching
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("got quit"),
Message::Move {x, y} => println!("got move {x}, {y}"),
Message::Write(s) => println!("got write {s:?}"),
Message::ChangeColor(r, g, b) => println!("got change_color {r}, {g}, {b}"),
}
}
}
let m = Message::Write(String::from("hello"));
m.call();
got write "hello"
fn main() {
enum Message {
Quit,
Move { x: i64, y: i64 },
Write(String),
ChangeColor(i64, i64, i64),
}
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("got quit"),
Message::Move {x, y} => println!("got move {x}, {y}"),
Message::Write(s) => println!("got write {s:?}"),
Message::ChangeColor(r, g, b) => println!("got change_color {r}, {g}, {b}"),
}
}
}
let m = Message::Write(String::from("hello"));
m.call();
}
Pattern Matching
match value {
PATTERN1 => expr1,
PATTERN2 => expr2,
PATTERN3 => expr3,
}
Pattern Matching
match value {
PATTERN1 => expr1,
PATTERN2 => expr2,
PATTERN3 => expr3,
}
- проверява дали стойността
valueотговаря на всеки от образцитеPATTERN1,PATTERN2, …
Pattern Matching
match value {
PATTERN1 => expr1,
PATTERN2 => expr2,
PATTERN3 => expr3,
}
- проверява дали стойността
valueотговаря на всеки от образцитеPATTERN1,PATTERN2, … - спира при първото намерено съвпадение и изпълнява израза в дясната част на ръкава
Pattern Matching
match value {
PATTERN1 => expr1,
PATTERN2 => expr2,
PATTERN3 => expr3,
}
- проверява дали стойността
valueотговаря на всеки от образцитеPATTERN1,PATTERN2, … - спира при първото намерено съвпадение и изпълнява израза в дясната част на ръкава
- образците трябва да покриват всички възможни стойности - трябва в поне един от ръкавите да има съвпадение
Pattern Matching
match value {
PATTERN1 => expr1,
PATTERN2 => expr2,
PATTERN3 => expr3,
}
- проверява дали стойността
valueотговаря на всеки от образцитеPATTERN1,PATTERN2, … - спира при първото намерено съвпадение и изпълнява израза в дясната част на ръкава
- образците трябва да покриват всички възможни стойности - трябва в поне един от ръкавите да има съвпадение
matchе израз - стойността му е резултата от изпълнения израз
Pattern Matching
Може да се съпоставя по стойност
let condition = true;
match condition {
true => println!("it's ok"),
false => println!("it's bad"),
}
it's ok
fn main() {
let condition = true;
match condition {
true => println!("it's ok"),
false => println!("it's bad"),
}
}
Pattern Matching
Образеца _ отговаря на всяка възможна стойност
let my_number = 14;
match my_number {
1 => println!("it's one"),
2 => println!("it's two"),
3 => println!("it's three"),
_ => println!("it's something else"),
}
it's something else
fn main() {
let my_number = 14;
match my_number {
1 => println!("it's one"),
2 => println!("it's two"),
3 => println!("it's three"),
_ => println!("it's something else"),
}
}
Pattern Matching
Може да се сравнява с интервали от стойности
let my_number = 14_u32;
match my_number {
0 => println!("none"),
1..3 => println!("small"),
3..=14 => println!("medium"),
15.. => println!("large"),
}
medium
fn main() {
let my_number = 14_u32;
match my_number {
0 => println!("none"),
1..3 => println!("small"),
3..=14 => println!("medium"),
15.. => println!("large"),
}
}
Pattern Matching
Съвпадналата стойност може да бъде присвоена на променлива с @
let my_number = 14_u32;
match my_number {
0 => println!("none"),
n @ 1..3 => println!("{n} is small"),
n @ 3..=14 => println!("{n} is medium"),
15.. => println!("too large"),
}
14 is medium
fn main() {
let my_number = 14_u32;
match my_number {
0 => println!("none"),
n @ 1..3 => println!("{n} is small"),
n @ 3..=14 => println!("{n} is medium"),
15.. => println!("too large"),
}
}
Pattern Matching
Име (identifier) съвпада на всяка стойност и присвоява стойността на променлива с това име. Все едно var @ _.
let my_number = 14_u32;
match my_number {
0 => println!("none"),
n @ 1..3 => println!("{n} is small"),
n @ 3..=14 => println!("{n} is medium"),
n => println!("{n} is too large"),
}
14 is medium
fn main() {
let my_number = 14_u32;
match my_number {
0 => println!("none"),
n @ 1..3 => println!("{n} is small"),
n @ 3..=14 => println!("{n} is medium"),
n => println!("{n} is too large"),
}
}
Pattern Matching
Име в образен винаги създава нова променлива, която засенчва съществуващи променливи.
Не може да се използва за съпоставяне със стойността на съществуваща променлива
#![allow(unreachable_patterns)]
let x = 5;
match 123 {
x => println!("it should be five; got x={x}"), // wrong
_ => println!("it's not five"), // wrong
}
it should be five; got x=123
#![allow(unreachable_patterns)]
fn main() {
let x = 5;
match 123 {
x => println!("it should be five; got x={x}"), // wrong
_ => println!("it's not five"), // wrong
}
}
Pattern Matching
Име в образен винаги създава нова променлива, която засенчва съществуващи променливи.
Не може да се използва за съпоставяне със стойността на съществуваща променлива
// #![allow(unreachable_patterns)]
let x = 5;
match 123 {
x => println!("it should be five; got x={x}"), // wrong
_ => println!("it's not five"), // wrong
}
warning: unreachable pattern --> src/bin/main_0992ff311fb9eb93ad5bdedc2e5e35d62833f5ba.rs:7:5 | 6 | x => println!("it should be five; got x={x}"), // wrong | - matches any value 7 | _ => println!("it's not five"), // wrong | ^ no value can reach this | note: there is a binding of the same name; if you meant to pattern match against the value of that binding, that is a feature of constants that is not available for `let` bindings --> src/bin/main_0992ff311fb9eb93ad5bdedc2e5e35d62833f5ba.rs:4:5 | 4 | let x = 5; | ^ = note: `#[warn(unreachable_patterns)]` on by default
fn main() {
let x = 5;
match 123 {
x => println!("it should be five; got x={x}"), // wrong
_ => println!("it's not five"), // wrong
}
}
Pattern Matching
enum FooBar {
Foo,
Bar,
}
let val = FooBar::Foo;
match val {
FooBar::Foo => println!("it's foo"),
FooBar::Bar => println!("it's bar"),
}
it's foo
fn main() {
enum FooBar {
Foo,
Bar,
}
let val = FooBar::Foo;
match val {
FooBar::Foo => println!("it's foo"),
FooBar::Bar => println!("it's bar"),
}
}
Pattern Matching
let coords = (3, 3);
match coords {
(0, 0) => println!("center"),
(0, y) => println!("vertical {y}"),
(x, 0) => println!("horizontal {x}"),
(x, y) => println!("nowhere in particular {x},{y}"),
}
nowhere in particular 3,3
fn main() {
let coords = (3, 3);
match coords {
(0, 0) => println!("center"),
(0, y) => println!("vertical {y}"),
(x, 0) => println!("horizontal {x}"),
(x, y) => println!("nowhere in particular {x},{y}"),
}
}
Pattern Matching
Съпоставяне на масиви и резени
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!")
}
}
Pattern Matching
.. отговаря на всичко останало от масива
let nums = &[1, 2, 3][..];
match nums {
[] => println!("empty"),
[a] => println!("just {a}"),
[x, ..] => println!("head is {x}"),
}
head is 1
fn main() {
let nums = &[1, 2, 3][..];
match nums {
[] => println!("empty"),
[a] => println!("just {a}"),
[x, ..] => println!("head is {x}"),
}
}
Pattern Matching
.. може да се комбинира с @
let nums = &[1, 2, 3][..];
match nums {
[] => println!("empty"),
[a] => println!("just {a}"),
[x, rest @ ..] => println!("head is {x}, tail is {rest:?}"),
}
head is 1, tail is [2, 3]
fn main() {
let nums = &[1, 2, 3][..];
match nums {
[] => println!("empty"),
[a] => println!("just {a}"),
[x, rest @ ..] => println!("head is {x}, tail is {rest:?}"),
}
}
Pattern Matching
.. работи и в началото или средата.
let nums = &[1, 2, 3, 4][..];
match nums {
[] => println!("empty"),
[a] => println!("just {a}"),
[first, mid @ .., last] => println!("{first} then {mid:?} then {last}"),
}
1 then [2, 3] then 4
fn main() {
let nums = &[1, 2, 3, 4][..];
match nums {
[] => println!("empty"),
[a] => println!("just {a}"),
[first, mid @ .., last] => println!("{first} then {mid:?} then {last}"),
}
}
Pattern Matching
Но може да се среща само веднъж в целия образец
let nums = &[1, 2, 3, 4][..];
match nums {
[] => println!("empty"),
[a] => println!("just {a}"),
[.., mid, ..] => println!("mid is {mid}"),
}
error: `..` can only be used once per slice pattern --> src/bin/main_cc2e630a367d1ea68231c8d5466227007d241c57.rs:6:15 | 6 | [.., mid, ..] => println!("mid is {mid}"), | -- ^^ can only be used once per slice pattern | | | previously used here error: could not compile `rust` (bin "main_cc2e630a367d1ea68231c8d5466227007d241c57") due to 1 previous error
fn main() {
let nums = &[1, 2, 3, 4][..];
match nums {
[] => println!("empty"),
[a] => println!("just {a}"),
[.., mid, ..] => println!("mid is {mid}"),
}
}
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
Алтернативност
Няколко алтернативни образци могат да бъдат сложени в един ръкав, разделени с |.
Ще бъдат пробвани от ляво надясно. Променливи могат да се прихващат, стига да присъстват във всеки образец и да са от един и същи тип.
enum FooBar {
Foo(u32),
Bar(u32),
}
let val = FooBar::Bar(13);
match val {
FooBar::Foo(10) | FooBar::Bar(10) => {
println!("десетка");
}
FooBar::Foo(n @ 0..=9) | FooBar::Bar(n) => {
println!("малък фуу или бар: {n}");
}
FooBar::Foo(_) => {
println!("голям фуу");
}
}
малък фуу или бар: 13
fn main() {
enum FooBar {
Foo(u32),
Bar(u32),
}
let val = FooBar::Bar(13);
match val {
FooBar::Foo(10) | FooBar::Bar(10) => {
println!("десетка");
}
FooBar::Foo(n @ 0..=9) | FooBar::Bar(n) => {
println!("малък фуу или бар: {n}");
}
FooBar::Foo(_) => {
println!("голям фуу");
}
}
}
Pattern Matching
Алтернативност
Интересен факт - позволено е да се добави символ | в началото на ръкава, който не прави нищо.
Но позволява кода да се подреди по този начин.
enum FooBar {
Foo(u32),
Bar(u32),
}
let val = FooBar::Bar(13);
match val {
| FooBar::Foo(10)
| FooBar::Bar(10) => {
println!("десетка");
}
| FooBar::Foo(n @ 0..=9)
| FooBar::Bar(n) => {
println!("малък фуу или бар: {n}");
}
FooBar::Foo(_) => {
println!("голям фуу");
}
}
малък фуу или бар: 13
fn main() {
enum FooBar {
Foo(u32),
Bar(u32),
}
let val = FooBar::Bar(13);
match val {
| FooBar::Foo(10)
| FooBar::Bar(10) => {
println!("десетка");
}
| FooBar::Foo(n @ 0..=9)
| FooBar::Bar(n) => {
println!("малък фуу или бар: {n}");
}
FooBar::Foo(_) => {
println!("голям фуу");
}
}
}
Pattern Matching
Ограничения - типовете трябва да съответстват
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, .. } => println!("Ко стаа, {name}"),
}
Ко стаа, Пешо
fn main() {
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, .. } => println!("Ко стаа, {name}"),
}
}
Pattern Matching
Ограничения - типовете трябва да съответстват
struct User {
name: String, // <- низ със собственост
age: u8
}
let user = User { name: "Пешо", age: 12 };
match user { // не е ок
User { name: "Пешо", age: _ } => println!("Ко стаа, Пешо"),
User { name: _, age: 12 } => println!("Ко стаа, лапе"),
User { name, .. } => println!("Ко стаа, {name}"),
}
error[E0308]: mismatched types --> src/bin/main_439783a543372c3358cdb023d2fa310f5cf4e04c.rs:7:25 | 7 | let user = User { name: "Пешо", age: 12 }; | ^^^^^^ expected `String`, found `&str` | help: try using a conversion method | 7 | let user = User { name: "Пешо".to_string(), age: 12 }; | ++++++++++++ error[E0308]: mismatched types --> src/bin/main_439783a543372c3358cdb023d2fa310f5cf4e04c.rs:10:18 | 9 | match user { // не е ок | ---- this expression has type `User` 10 | User { name: "Пешо", age: _ } => println!("Ко стаа, Пешо"), | ^^^^^^ expected `String`, found `&str` For more information about this error, try `rustc --explain E0308`. error: could not compile `rust` (bin "main_439783a543372c3358cdb023d2fa310f5cf4e04c") due to 2 previous errors
fn main() {
struct User {
name: String, // <- низ със собственост
age: u8
}
let user = User { name: "Пешо", age: 12 };
match user { // не е ок
User { name: "Пешо", age: _ } => println!("Ко стаа, Пешо"),
User { name: _, age: 12 } => println!("Ко стаа, лапе"),
User { name, .. } => println!("Ко стаа, {name}"),
}
}
Destructuring
Destructuring
let bindings
Повечето от образците, поддържани от match, могат да се използват и за разбиране на стойност в let клаузи.
Дясната страна трябва винаги да може да се съпостави на лявата - irrefutable binding.
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
Повечето от образците, поддържани от match, могат да се използват и за разбиране на стойност в let клаузи.
Дясната страна трябва винаги да може да се съпостави на лявата - irrefutable binding.
let User { name: name_var, .. } = user;
println!("{}", name_var);
let User { name, .. } = user;
println!("{}", name);
fn main() {
#[derive(Default)] struct User { name: String, email: String }
let user = User::default();
let User { name: name_var, .. } = user;
println!("{}", name_var);
let user = User::default();
let User { name, .. } = user;
println!("{}", name);
}
More control flow
if let
if let PATTERN = value {
code()
}
Еквиваленто на долния match.
Но спестява едно ниво на индентация и няколко реда
match value {
PATTERN => {
code()
}
_ => {}
}
More control flow
while let
А защо не и while let:
while let PATTERN = expr() {
code()
}
Еквивалентно на
loop {
match expr() {
PATTERN => {
code()
}
_ => {
break
}
}
}
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);
}
Option
Non-zero оптимизация
За много типове нула/null е невалидна стойност.
Или съдържат поле, за което е невалидна стойност (напр ptr в String).
Ако такъв тип се използва в enum, компилатора може да използва свободния слот, за да помести другите варианти.
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::());
}
Option
Non-zero оптимизация
Това много често се случва при Option.
При всички тези случаи Option е zero cost abstraction.
use std::mem;
fn main() {
println!("&T: {:?} vs {:?}", mem::size_of::<&u32>(), mem::size_of::<Option<&u32>>());
println!("Box<T>: {:?} vs {:?}", mem::size_of::<Box<u32>>(), mem::size_of::<Option<Box<u32>>>());
println!("&str: {:?} vs {:?}", mem::size_of::<&str>(), mem::size_of::<Option<&str>>());
println!("String: {:?} vs {:?}", mem::size_of::<String>(), mem::size_of::<Option<String>>());
println!("Vec<_>: {:?} vs {:?}", mem::size_of::<Vec<u32>>(), mem::size_of::<Option<Vec<u32>>>());
println!("HashMap<_, _>: {:?} vs {:?}", mem::size_of::<HashMap<u32, u32>>(), mem::size_of::<Option<HashMap<u32, u32>>>());
}
&T: 8 vs 8 Box<T>: 8 vs 8 &str: 16 vs 16 String: 24 vs 24 Vec<_>: 24 vs 24 HashMap<_, _>: 48 vs 48
use std::collections::HashMap;
use std::mem;
fn main() {
println!("&T: {:?} vs {:?}", mem::size_of::<&u32>(), mem::size_of::
Result
Друг много често срещан тип е Result.
В Rust няма exceptions - функции които могат да се провалят връщат Result
enum Result<T, E> {
Ok(T),
Err(E),
}
fn main() {}
enum Result {
Ok(T),
Err(E),
}
Option и Result
Извличане на стойност
- най-добре чрез pattern matching
- трябва да се справим със случая когато няма стойност -
NoneприOption - или има грешка -
ErrприResult
let some_number = Some(5);
let double = match some_number {
Some(val) => val + val,
None => 0,
};
println!("{:?}", double);
10
fn main() {
let some_number = Some(5);
let double = match some_number {
Some(val) => val + val,
None => 0,
};
println!("{:?}", double);
}
Option и Result
Извличане на стойност
unwrap()expect(msg)
Option и Result
Извличане на стойност
unwrap()expect(msg)- лесен начин да извлечем стойността, ако сме сигурни, че има такава
Option и Result
Извличане на стойност
unwrap()expect(msg)- лесен начин да извлечем стойността, ако сме сигурни, че има такава
- в противен случай ще panic-не
Option и Result
Извличане на стойност
unwrap()expect(msg)- лесен начин да извлечем стойността, ако сме сигурни, че има такава
- в противен случай ще panic-не
- което убива програмата със съобщение за грешка
Option и Result
Извличане на стойност
unwrap()expect(msg)- лесен начин да извлечем стойността, ако сме сигурни, че има такава
- в противен случай ще panic-не
- което убива програмата със съобщение за грешка
unwrapиexpectчесто се използват и за rapid prototyping
let some_number = Some(5);
let val = some_number.unwrap();
let double = val + val;
println!("{:?}", double);
10
fn main() {
let some_number = Some(5);
let val = some_number.unwrap();
let double = val + val;
println!("{:?}", double);
}
Итерация
Итерация
let numbers = vec![11, 22, 33, 44, 55];
for n in numbers.iter() {
print!("{} ", n);
}
11 22 33 44 55
fn main() {
let numbers = vec![11, 22, 33, 44, 55];
for n in numbers.iter() {
print!("{} ", n);
}
println!();
}
Итерация
for c in "Здравей! 😊" {
// ...
}
error[E0277]: `&str` is not an iterator --> src/bin/main_d4d96cd45194a2a7f7b2fdd29a913a03c9c66432.rs:2:10 | 2 | for c in "Здравей! 😊" { | ^^^^^^^^^^^^^ `&str` is not an iterator; try calling `.chars()` or `.bytes()` | = help: the trait `Iterator` is not implemented for `&str` = note: required for `&str` to implement `IntoIterator` For more information about this error, try `rustc --explain E0277`. error: could not compile `rust` (bin "main_d4d96cd45194a2a7f7b2fdd29a913a03c9c66432") due to 1 previous error
fn main() {
for c in "Здравей! 😊" {
// ...
}
}
Итерация
for b in "Здравей! 😊".bytes() {
print!("{:02x} ", b);
}
d0 97 d0 b4 d1 80 d0 b0 d0 b2 d0 b5 d0 b9 21 20 f0 9f 98 8a
fn main() {
for b in "Здравей! 😊".bytes() {
print!("{:02x} ", b);
}
println!();
}
Итерация
for c in "Здравей! 😊".chars() {
print!("{:?} ", c);
}
'З' 'д' 'р' 'а' 'в' 'е' 'й' '!' ' ' '😊'
fn main() {
for c in "Здравей! 😊".chars() {
print!("{:?} ", c);
}
println!();
}
Итерация
for s in "Здравей свят! 😊".split_whitespace() {
println!("{:?} ", s);
}
"Здравей" "свят!" "😊"
fn main() {
for s in "Здравей свят! 😊".split_whitespace() {
println!("{:?} ", s);
}
println!();
}
Итерация
for value in iterator {
code
}
Итерация
for value in iterator {
code
}
- итератор е тип, който имплементира
trait Iterator
Итерация
for value in iterator {
code
}
- итератор е тип, който имплементира
trait Iterator
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
fn main() {}
trait Iterator {
type Item;
fn next(&mut self) -> Option;
}
Итерация
- итератора е структура, която пази текущото състояние на итерацията
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
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
}
Итерация
- итератора има метод
fn next(&mut self) -> Option<Item> - който връща следващия елемент, или
None
let mut words = "one two three".split_whitespace(); // std::str::SplitWhitespace
println!("{:?}", words.next());
println!("{:?}", words.next());
println!("{:?}", words.next());
println!("{:?}", words.next());
Some("one") Some("two") Some("three") None
fn main() {
let mut words = "one two three".split_whitespace(); // std::str::SplitWhitespace
println!("{:?}", words.next());
println!("{:?}", words.next());
println!("{:?}", words.next());
println!("{:?}", words.next());
}
Итерация
forцикъла извикваnextвърху итератора, докато не получиNone
for word in "one two three".split_whitespace() {
println!("{}", word);
}
// приблизителен desugaring
{
let mut iter = "one two three".split_whitespace();
loop {
match iter.next() {
Some(word) => {
println!("{}", word);
},
None => {
break;
}
}
}
}
fn main() {
for word in "one two three".split_whitespace() {
println!("{}", word);
}
// приблизителен desugaring
{
let mut iter = "one two three".split_whitespace();
loop {
match iter.next() {
Some(word) => {
println!("{}", word);
},
None => {
break;
}
}
}
}
}
Итерация
- корекция -
forцикъла не приема итератор, а нещо, което може да се ковертира до итератор - чрез
trait IntoIterator, който предоставя методinto_iter
for word in "one two three".split_whitespace() {
println!("{}", word);
}
// малко по-точен desugaring
{
let mut iter = ("one two three".split_whitespace()).into_iter();
loop {
match iter.next() {
Some(word) => {
println!("{}", word);
},
None => {
break;
}
}
}
}
fn main() {
for word in "one two three".split_whitespace() {
println!("{}", word);
}
// малко по-точен desugaring
{
let mut iter = ("one two three".split_whitespace()).into_iter();
loop {
match iter.next() {
Some(word) => {
println!("{}", word);
},
None => {
break;
}
}
}
}
}
Итерация
- този desugaring можем да го напишем по-кратко с while let
Итерация
- този desugaring можем да го напишем по-кратко с while let
for word in "one two three".split_whitespace() {
println!("{}", word);
}
// захаросан desugaring ??
{
let mut iter = ("one two three".split_whitespace()).into_iter();
while let Some(word) = iter.next() {
println!("{}", word);
}
}
fn main() {
for word in "one two three".split_whitespace() {
println!("{}", word);
}
// захаросан desugaring ??
{
let mut iter = ("one two three".split_whitespace()).into_iter();
while let Some(word) = iter.next() {
println!("{}", word);
}
}
}
Итерация
Пример
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!();
}
}
Итерация
IntoIterator
- за всеки итератор
into_iterе идентитет - връща себе си - но това позволява итератиране по типове, които не са итератори
let numbers = vec![11, 22, 33, 44, 55];
for num in &numbers[..] {
}
// vs
for num in numbers.iter() {
}
fn main() {
let numbers = vec![11, 22, 33, 44, 55];
for num in &numbers[..] {
}
// vs
for num in numbers.iter() {
}
}
Итерация
IntoIterator
- конвенция
- много типове имат методи
iterилиiter_mut, където
x.iter()
// е еквивалентно на
(&x).into_iter()
x.iter_mut()
// е еквиваленто на
(&mut x).into_iter()
Итерация
IntoIterator
| израз | тип на итератора |
|---|---|
for x in &[1, 2, 3] |
std::slice::Iter |
for x in [1, 2, 3].iter() |
std::slice::Iter |
for x in &vec![1, 2, 3] |
std::slice::Iter |
for x in vec![1, 2, 3].iter() |
std::slice::Iter |
for x in &mut [1, 2, 3] |
std::slice::IterMut |
for x in [1, 2, 3].iter_mut() |
std::slice::IterMut |
Методи на итератори
next()- очевидно, връща следващия елемент
Методи на итератори
next()- очевидно, връща следващия елементnth(n)- връща n-тия пореден елементlast()- връща последния елемент
Методи на итератори
next()- очевидно, връща следващия елементnth(n)- връща n-тия пореден елементlast()- връща последния елементcount()- връща броя на елементите
Методи на итератори
next()- очевидно, връща следващия елементnth(n)- връща n-тия пореден елементlast()- връща последния елементcount()- връща броя на елементитеskip(n)skip_while(fn)- пропуска първите n на брой елементи, или първите, отговарищи на условието
Методи на итератори
next()- очевидно, връща следващия елементnth(n)- връща n-тия пореден елементlast()- връща последния елементcount()- връща броя на елементитеskip(n)skip_while(fn)- пропуска първите n на брой елементи, или първите, отговарищи на условиетоtake(n)take_while(fn)- връща само първите n на брой елемент, или първите, отговарящи на условието
Методи на итератори
collect()- връща колекция, съдържаща елементите от итератора
Методи на итератори
collect()- връща колекция, съдържаща елементите от итератора- трябва да се окаже типа на резултата - защото колекцията може да бъде всякаква
Vec,VecDeque,HashSet, …HashMap,BTreeMap, … - ако итератора е по двойки
Методи на итератори
collect()- връща колекция, съдържаща елементите от итератора- трябва да се окаже типа на резултата - защото колекцията може да бъде всякаква
Vec,VecDeque,HashSet, …HashMap,BTreeMap, … - ако итератора е по двойки
let text = "Здравей! 😊";
let text_chars: Vec<char> = text.chars().collect();
println!("{:?}", text_chars);
['З', 'д', 'р', 'а', 'в', 'е', 'й', '!', ' ', '😊']
fn main() {
let text = "Здравей! 😊";
let text_chars: Vec = text.chars().collect();
println!("{:?}", text_chars);
}
Методи на итератори
- нужно е да се окаже само типа на колекцията
- типа на елементите на колекцията обикновенно може да се отгатне от контекста
let text = "Здравей! 😊";
let text_chars: Vec<_> = text.chars().collect();
// ^^^
println!("{:?}", text_chars);
['З', 'д', 'р', 'а', 'в', 'е', 'й', '!', ' ', '😊']
fn main() {
let text = "Здравей! 😊";
let text_chars: Vec<_> = text.chars().collect();
// ^^^
println!("{:?}", text_chars);
}
Методи на итератори
Методи на итератори
map(fn)
Методи на итератори
map(fn)filter(fn)
Методи на итератори
map(fn)filter(fn)fold(fn)
Методи на итератори
map(fn)filter(fn)fold(fn)- …
Методи на итератори
map(fn)filter(fn)fold(fn)- …
- тук вече става интересно
- но ще ги разглеждаме друг път