Често срещани типажи, итератори и анонимни функции
10 ноември 2022
Често използвани типажи
Често използвани типажи
Стандартната библиотека дефинира често използвани типажи
Често използвани типажи
Стандартната библиотека дефинира често използвани типажи
Голяма част от Rust екосистемата разчита на тях
Често използвани типажи
Стандартната библиотека дефинира често използвани типажи
Голяма част от Rust екосистемата разчита на тях
Само ние можем да имплементираме стандартните trait-ове за наши типове
Често използвани типажи
Стандартната библиотека дефинира често използвани типажи
Голяма част от Rust екосистемата разчита на тях
Само ние можем да имплементираме стандартните trait-ове за наши типове
Затова, ако пишем библиотека, е добре да имплементираме всички стандартни trait-ове, които можем
Често използвани типажи
Списък
- Copy
- Clone
- Eq
- PartialEq
- Ord
- PartialOrd
- Hash
- Debug
- Display
- Default
Clone
trait Clone {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) { ... }
}
Clone
trait Clone {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) { ... }
}
- Създава копие на обекта
Clone
trait Clone {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) { ... }
}
- Създава копие на обекта
- Позволява да си дефинираме собствена логика за копирането
Clone
trait Clone {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) { ... }
}
- Създава копие на обекта
- Позволява да си дефинираме собствена логика за копирането
- Поддържа
#[derive(Clone)], ако всички полета имплементиратClone
Clone
trait Clone {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) { ... }
}
- Създава копие на обекта
- Позволява да си дефинираме собствена логика за копирането
- Поддържа
#[derive(Clone)], ако всички полета имплементиратClone - Имплементацията от derive извиква
cloneна всички полета рекурсивно
Clone
trait Clone {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) { ... }
}
- Създава копие на обекта
- Позволява да си дефинираме собствена логика за копирането
- Поддържа
#[derive(Clone)], ако всички полета имплементиратClone - Имплементацията от derive извиква
cloneна всички полета рекурсивно - Рядко ще се налага да правим ръчна имплементация на
Clone, защото не работим с гола памет!
Copy
trait Copy: Clone { }
Copy
trait Copy: Clone { }
- Marker trait
Copy
trait Copy: Clone { }
- Marker trait
- Показва, че стойността може да се копира чрез копиране на паметта байт по байт т.е.
memcopy
Copy
trait Copy: Clone { }
- Marker trait
- Показва, че стойността може да се копира чрез копиране на паметта байт по байт т.е.
memcopy - Променя се семантиката за присвояване на стойност от преместване (move) на копиране (copy)
Copy
trait Copy: Clone { }
- Marker trait
- Показва, че стойността може да се копира чрез копиране на паметта байт по байт т.е.
memcopy - Променя се семантиката за присвояване на стойност от преместване (move) на копиране (copy)
- Изисква
Cloneда е имплементиран за съответния тип.
Copy
trait Copy: Clone { }
- Marker trait
- Показва, че стойността може да се копира чрез копиране на паметта байт по байт т.е.
memcopy - Променя се семантиката за присвояване на стойност от преместване (move) на копиране (copy)
- Изисква
Cloneда е имплементиран за съответния тип. - Може да се добави с
#[derive(Copy)]
Copy
trait Copy: Clone { }
- Marker trait
- Показва, че стойността може да се копира чрез копиране на паметта байт по байт т.е.
memcopy - Променя се семантиката за присвояване на стойност от преместване (move) на копиране (copy)
- Изисква
Cloneда е имплементиран за съответния тип. - Може да се добави с
#[derive(Copy)] - Или като цяло с
Clone-#[derive(Copy, Clone)], поредността няма значение
Copy
trait Copy: Clone { }
- Marker trait
- Показва, че стойността може да се копира чрез копиране на паметта байт по байт т.е.
memcopy - Променя се семантиката за присвояване на стойност от преместване (move) на копиране (copy)
- Изисква
Cloneда е имплементиран за съответния тип. - Може да се добави с
#[derive(Copy)] - Или като цяло с
Clone-#[derive(Copy, Clone)], поредността няма значение
Можем да имплементираме Copy само ако:
Copy
trait Copy: Clone { }
- Marker trait
- Показва, че стойността може да се копира чрез копиране на паметта байт по байт т.е.
memcopy - Променя се семантиката за присвояване на стойност от преместване (move) на копиране (copy)
- Изисква
Cloneда е имплементиран за съответния тип. - Може да се добави с
#[derive(Copy)] - Или като цяло с
Clone-#[derive(Copy, Clone)], поредността няма значение
Можем да имплементираме Copy само ако:
- всички полета са
Copy
Copy
trait Copy: Clone { }
- Marker trait
- Показва, че стойността може да се копира чрез копиране на паметта байт по байт т.е.
memcopy - Променя се семантиката за присвояване на стойност от преместване (move) на копиране (copy)
- Изисква
Cloneда е имплементиран за съответния тип. - Може да се добави с
#[derive(Copy)] - Или като цяло с
Clone-#[derive(Copy, Clone)], поредността няма значение
Можем да имплементираме Copy само ако:
- всички полета са
Copy - типа няма дефиниран деструктор (т.е. не е
Drop)
Drop
pub trait Drop {
fn drop(&mut self);
}
Drop
pub trait Drop {
fn drop(&mut self);
}
- Позволява да дефинираме деструктори
Drop
pub trait Drop {
fn drop(&mut self);
}
- Позволява да дефинираме деструктори
- Метода се извиква автоматично, когато обекта излезе от scope
Drop
pub trait Drop {
fn drop(&mut self);
}
- Позволява да дефинираме деструктори
- Метода се извиква автоматично, когато обекта излезе от scope
- Не може да се извика ръчно
Drop
pub trait Drop {
fn drop(&mut self);
}
- Позволява да дефинираме деструктори
- Метода се извиква автоматично, когато обекта излезе от scope
- Не може да се извика ръчно
- Вика се
dropна всяко поле рекурсивно, ако имплементираDrop
Drop
pub trait Drop {
fn drop(&mut self);
}
- Позволява да дефинираме деструктори
- Метода се извиква автоматично, когато обекта излезе от scope
- Не може да се извика ръчно
- Вика се
dropна всяко поле рекурсивно, ако имплементираDrop - Можем да използваме
std::mem::dropза да "накараме" drop-ване (просто move-ва стойността в себе си и приключва)
Default
trait Default {
fn default() -> Self;
}
Default
trait Default {
fn default() -> Self;
}
- Позволява създаване на обект със стойност по подразбиране
Default
trait Default {
fn default() -> Self;
}
- Позволява създаване на обект със стойност по подразбиране
- Може да се добави с
#[derive(Default)], ако всички полета имплементиратDefault
Default
trait Default {
fn default() -> Self;
}
- Позволява създаване на обект със стойност по подразбиране
- Може да се добави с
#[derive(Default)], ако всички полета имплементиратDefault - Q:
Defaultилиfn new() -> Self
Default
trait Default {
fn default() -> Self;
}
- Позволява създаване на обект със стойност по подразбиране
- Може да се добави с
#[derive(Default)], ако всички полета имплементиратDefault - Q:
Defaultилиfn new() -> Self - A: и двете
Hash
Hash
- Използва се от типове и функции, които използват хеширане
Hash
Hash
Hash
Display & Debug
Вече сме виждали #[derive(Debug)], сега ще разгледаме как да си имлементираме собствени Display и Debug
Те ни позволяват format!, println! и подобни макроси да работят за наш тип
struct MagicTrick {
description: String,
secrets: Vec<String>,
skills: Vec<String>,
}
let trick = MagicTrick {
description: String::from("Изчезваща монета"),
secrets: vec![String::from("Монетата се прибира в ръкава")],
skills: vec![String::from("Бързи ръце"), String::from("Заблуда")],
};
println!("{}", trick);
println!("===");
println!("{:?}", trick);
Магически трик "Изчезваща монета" === MagicTrick { description: "Изчезваща монета", secrets: ["Монетата се прибира в ръкава"], skills: ["Бързи ръце", "Заблуда"] }
use std::fmt::{self, Display, Formatter};
impl Display for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Магически трик {:?}", self.description)
}
}
#[derive(Debug)]
struct MagicTrick {
description: String,
secrets: Vec,
skills: Vec,
}
fn main() {
let trick = MagicTrick {
description: String::from("Изчезваща монета"),
secrets: vec![String::from("Монетата се прибира в ръкава")],
skills: vec![String::from("Бързи ръце"), String::from("Заблуда")],
};
println!("{}", trick);
println!("===");
println!("{:?}", trick);
}
Display
Display
- Използва се от placeholder-a
{}за форматиране на стойност, която ще се показва на потребителя
Display
- Използва се от placeholder-a
{}за форматиране на стойност, която ще се показва на потребителя - Не може да се derive-не за разлика от
Debug
Display
use std::fmt::{self, Display, Formatter};
impl Display for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Магически трик {:?}", self.description)
}
}
#![allow(dead_code)]
struct MagicTrick { description: String }
fn main() {}
use std::fmt::{self, Display, Formatter};
impl Display for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Магически трик {:?}", self.description)
}
}
Display
Нека да разбием примера и да видим какво oзначават новите неща
Макрос write
write!(f, "Магически трик {:?}", self.description)
Макрос write
write!(f, "Магически трик {:?}", self.description)
- Подобно на
print!иformat!
Макрос write
write!(f, "Магически трик {:?}", self.description)
- Подобно на
print!иformat! - Записва форматиран текст в структура, която имплементира
std::fmt::Writeилиstd::io::Write
Formatter структура
- Записваме форматирания текст в нея
Formatter структура
- Записваме форматирания текст в нея
- Съдържа набор от полезни функции за форматира като
pad,precision,width,debug_structи други
Formatter структура
- Записваме форматирания текст в нея
- Съдържа набор от полезни функции за форматира като
pad,precision,width,debug_structи други - Не можем да я конструираме, стандартната библиотека ни я подава като извиква форматъра
Display
struct MagicTrick {
description: String,
secrets: Vec<String>,
skills: Vec<String>,
}
let trick = MagicTrick {
description: String::from("Изчезваща монета"),
secrets: vec![String::from("Монетата се прибира в ръкава")],
skills: vec![String::from("Бързи ръце"), String::from("Заблуда")],
};
println!("{}", trick);
Магически трик "Изчезваща монета"
#![allow(dead_code)]
use std::fmt::{self, Display, Formatter};
impl Display for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Магически трик {:?}", self.description)
}
}
struct MagicTrick {
description: String,
secrets: Vec,
skills: Vec,
}
fn main() {
let trick = MagicTrick {
description: String::from("Изчезваща монета"),
secrets: vec![String::from("Монетата се прибира в ръкава")],
skills: vec![String::from("Бързи ръце"), String::from("Заблуда")],
};
println!("{}", trick);
}
Debug
Debug
- Използва се от placeholder-a
{:?}за форматиране на стойност, която ще се показва само с цел debug
Debug
- Използва се от placeholder-a
{:?}за форматиране на стойност, която ще се показва само с цел debug - Както знаете
#[derive(Debug)]имплементира версия по подразбиране
Debug
Може да напишем и собствена имплементация
use std::fmt::{self, Debug, Formatter};
impl Debug for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write! {
f,
r#"Трик
Описание: {:?}
Тайни: {:?}
Умения: {:?}"#,
self.description,
self.secrets,
self.skills
}
}
}
#![allow(dead_code)]
fn main() {}
struct MagicTrick {
description: String,
secrets: Vec,
skills: Vec
}
use std::fmt::{self, Debug, Formatter};
impl Debug for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write! {
f,
r#"Трик
Описание: {:?}
Тайни: {:?}
Умения: {:?}"#,
self.description,
self.secrets,
self.skills
}
}
}
Display & Debug
struct MagicTrick {
description: String,
secrets: Vec<String>,
skills: Vec<String>,
}
let trick = MagicTrick {
description: String::from("Изчезваща монета"),
secrets: vec![String::from("Монетата се прибира в ръкава")],
skills: vec![String::from("Бързи ръце"), String::from("Заблуда")],
};
println!("{}", trick);
println!("===");
println!("{:?}", trick);
Магически трик "Изчезваща монета" === Трик Описание: "Изчезваща монета" Тайни: ["Монетата се прибира в ръкава"] Умения: ["Бързи ръце", "Заблуда"]
#![allow(dead_code)]
use std::fmt::{self, Display, Debug, Formatter};
impl Display for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Магически трик {:?}", self.description)
}
}
impl Debug for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write! { f,
r#"Трик
Описание: {:?}
Тайни: {:?}
Умения: {:?}"#,
self.description, self.secrets, self.skills
}
}
}
struct MagicTrick {
description: String,
secrets: Vec,
skills: Vec,
}
fn main() {
let trick = MagicTrick {
description: String::from("Изчезваща монета"),
secrets: vec![String::from("Монетата се прибира в ръкава")],
skills: vec![String::from("Бързи ръце"), String::from("Заблуда")],
};
println!("{}", trick);
println!("===");
println!("{:?}", trick);
}
Предефиниране на оператори
Операторите се дефинират с trait-ове
Видяхме trait-а Add, с който дефинираме оператора +
trait Add<RHS = Self> {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
Предефиниране на оператори
Примери
Add,Sub,Mul,Div,RemBitAnd,BitOr,BitXor,Shl,Shr*Assign(AddAssign,SubAssign, и т.н.)Neg,NotIndexIndexMut
Предефиниране на оператори
PartialEq
trait PartialEq<Rhs = Self> where Rhs: ?Sized {
fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool { ... }
}
Предефиниране на оператори
PartialEq
trait PartialEq<Rhs = Self> where Rhs: ?Sized {
fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool { ... }
}
- Дефинира операторите
==и!=
Предефиниране на оператори
PartialEq
trait PartialEq<Rhs = Self> where Rhs: ?Sized {
fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool { ... }
}
- Дефинира операторите
==и!= - Не е задължително
a == aда върнеtrue
Предефиниране на оператори
PartialEq
trait PartialEq<Rhs = Self> where Rhs: ?Sized {
fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool { ... }
}
- Дефинира операторите
==и!= - Не е задължително
a == aда върнеtrue assert_eq!(::std::f64::NAN == ::std::f64::NAN, false);
Предефиниране на оператори
Eq
trait Eq: PartialEq<Self> { }
Предефиниране на оператори
Eq
trait Eq: PartialEq<Self> { }
- Marker trait
Предефиниране на оператори
Eq
trait Eq: PartialEq<Self> { }
- Marker trait
- Задължава
a == aда еtrue
Предефиниране на оператори
PartialOrd
trait PartialOrd<Rhs = Self>: PartialEq<Rhs> where Rhs: ?Sized {
fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>;
fn lt(&self, other: &Rhs) -> bool { ... }
fn le(&self, other: &Rhs) -> bool { ... }
fn gt(&self, other: &Rhs) -> bool { ... }
fn ge(&self, other: &Rhs) -> bool { ... }
}
enum Ordering {
Less,
Equal,
Greater,
}
Предефиниране на оператори
PartialOrd
Дефинира операторите < <= > >=
PartialOrd дефинира частична наредба
assert_eq!(::std::f64::NAN < 0.0, false);
assert_eq!(::std::f64::NAN >= 0.0, false);
fn main() {
assert_eq!(::std::f64::NAN < 0.0, false);
assert_eq!(::std::f64::NAN >= 0.0, false);
}
Предефиниране на оператори
Ord
trait Ord: Eq + PartialOrd<Self> {
fn cmp(&self, other: &Self) -> Ordering;
fn max(self, other: Self) -> Self { ... }
fn min(self, other: Self) -> Self { ... }
}
Дефинира тотална наредба т.е само едно от a < b, a == b, a > b е изпълнено.
Итератори

Итератори
Итераторите имплементират trait, който изглежда така:
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// ... predefined iterator methods ...
}
#![allow(dead_code)]
fn main() {}
trait Iterator {
type Item;
fn next(&mut self) -> Option;
// ... predefined iterator methods ...
}
Итератори
Ето и как може да си имплементиране собствен итератор
struct OneTwoThree {
state: u32,
}
impl OneTwoThree {
fn new() -> Self {
Self { state: 0 }
}
}
impl Iterator for OneTwoThree {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.state < 3 {
self.state += 1;
Some(self.state)
} else {
None
}
}
}
#![allow(dead_code)]
fn main() {}
struct OneTwoThree {
state: u32,
}
impl OneTwoThree {
fn new() -> Self {
Self { state: 0 }
}
}
impl Iterator for OneTwoThree {
type Item = u32;
fn next(&mut self) -> Option {
if self.state < 3 {
self.state += 1;
Some(self.state)
} else {
None
}
}
}
Итератори
fn main() {
let mut iter = OneTwoThree::new();
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
// ...
}
Some(1) Some(2) Some(3) None None
#![allow(dead_code)]
struct OneTwoThree {
state: u32,
}
impl Iterator for OneTwoThree {
type Item = u32;
fn next(&mut self) -> Option {
if self.state < 3 { self.state += 1 ; Some(self.state) } else { None }
}
}
impl OneTwoThree {
fn new() -> Self {
Self { state: 0 }
}
}
fn main() {
let mut iter = OneTwoThree::new();
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
// ...
}
Итератори
IntoIterator
Указва как може един тип да се конвертира до итератор.
trait IntoIterator
{
type Item;
type IntoIter: Iterator<Item=Self::Item>;
fn into_iter(self) -> Self::IntoIter;
}
Итератори
IntoIterator
let v = vec![1, 2, 3];
let mut iter = v.into_iter();
while let Some(i) = iter.next() {
println!("{}", i);
}
1 2 3
#![allow(dead_code)]
fn main() {
let v = vec![1, 2, 3];
let mut iter = v.into_iter();
while let Some(i) = iter.next() {
println!("{}", i);
}
}
Итератори
IntoIterator
Също така получаваме и благото да използваме типа директно във for-in цикли
Тъй като векторите имплементират този типаж, следните два примера са еднакви
let v = vec![1, 2, 3];
for i in v.into_iter() {
println!("{}", i);
}
1 2 3
#![allow(dead_code)]
fn main() {
let v = vec![1, 2, 3];
for i in v.into_iter() {
println!("{}", i);
}
}
let v = vec![1, 2, 3];
for i in v {
println!("{}", i);
}
1 2 3
#![allow(dead_code)]
fn main() {
let v = vec![1, 2, 3];
for i in v {
println!("{}", i);
}
}Итератори
for-in цикъл
Нека отбележим и доста важен факт, че всеки итератор има имплементиран IntoIterator
impl<I: Iterator> IntoIterator for I {
type Item = I::Item;
type IntoIter = I;
fn into_iter(self) -> I {
self
}
}
Итератори
for-in цикъл
let values = vec![1, 2, 3];
for x in values {
println!("{}", x);
}
1 2 3
#![allow(dead_code)]
fn main() {
let values = vec![1, 2, 3];
for x in values {
println!("{}", x);
}
}
Rust генерира следният код зад всеки for-in цикъл:
let values = vec![1, 2, 3];
{
let result = match IntoIterator::into_iter(values) {
mut iter => loop {
let next;
match iter.next() {
Some(val) => next = val,
None => break,
};
let x = next;
let () = { println!("{}", x); };
},
};
result
}
1 2 3
#![allow(dead_code)]
fn main() {
let values = vec![1, 2, 3];
{
let result = match IntoIterator::into_iter(values) {
mut iter => loop {
let next;
match iter.next() {
Some(val) => next = val,
None => break,
};
let x = next;
let () = { println!("{}", x); };
},
};
result
}
}
Итератори
FromIterator
Обратно на IntoIterator, FromIterator се използва за да укаже как един итератор да се конвертира до тип, най-често структура от данни.
trait FromIterator<A>: Sized {
fn from_iter<T: IntoIterator<Item=A>>(iter: T) -> Self;
}
Итератори
FromIterator
Обратно на IntoIterator, FromIterator се използва за да укаже как един итератор да се конвертира до тип, най-често структура от данни.
trait FromIterator<A>: Sized {
fn from_iter<T: IntoIterator<Item=A>>(iter: T) -> Self;
}
Ще видим как това е полезно при итератор адаптeрите.
Итератори
Vec
Има две интересни имплементации на Iterator за Vec, освен стандартната
impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
Итератори
Vec
които позволяват взаимно заменяемия код
let v = vec![1, 2, 3];
for i in v.iter() {
println!("{}", i);
}
1 2 3
fn main() {
let v = vec![1, 2, 3];
for i in v.iter() {
println!("{}", i);
}
}
let v = vec![1, 2, 3];
for i in &v {
println!("{}", i);
}
1 2 3
fn main() {
let v = vec![1, 2, 3];
for i in &v {
println!("{}", i);
}
}Итератори
Vec
както и mutable версията
let mut v = vec![1, 2, 3];
for i in v.iter_mut() {
*i += 1;
}
println!("{:?}", v);
[2, 3, 4]
fn main() {
let mut v = vec![1, 2, 3];
for i in v.iter_mut() {
*i += 1;
}
println!("{:?}", v);
}
let mut v = vec![1, 2, 3];
for i in &mut v {
*i += 1;
}
println!("{:?}", v);
[2, 3, 4]
fn main() {
let mut v = vec![1, 2, 3];
for i in &mut v {
*i += 1;
}
println!("{:?}", v);
}Итератори
Адаптери

Итератори
Адаптери
let v = vec![1, 2, 3];
let iter = v.into_iter().map(|x| x + 1).filter(|&x| x < 4);
for i in iter {
println!("{}", i);
}
2 3
fn main() {
let v = vec![1, 2, 3];
let iter = v.into_iter().map(|x| x + 1).filter(|&x| x < 4);
for i in iter {
println!("{}", i);
}
}
Итератори
Адаптери
let v = vec![1, 2, 3];
let iter = v.into_iter().map(|x| x + 1).filter(|&x| x < 4);
for i in iter {
println!("{}", i);
}
2 3
fn main() {
let v = vec![1, 2, 3];
let iter = v.into_iter().map(|x| x + 1).filter(|&x| x < 4);
for i in iter {
println!("{}", i);
}
}
- Това са функции които взимат итератор и връщат нов итератор.
Итератори
Адаптери
let v = vec![1, 2, 3];
let iter = v.into_iter().map(|x| x + 1).filter(|&x| x < 4);
for i in iter {
println!("{}", i);
}
2 3
fn main() {
let v = vec![1, 2, 3];
let iter = v.into_iter().map(|x| x + 1).filter(|&x| x < 4);
for i in iter {
println!("{}", i);
}
}
- Това са функции които взимат итератор и връщат нов итератор.
- Най-често правят трансформации на данните.
Итератори
Адаптери
let v = vec![1, 2, 3];
let v = v
.into_iter()
.map(|x| x + 1)
.filter(|&x| x < 4)
.collect::<Vec<_>>();
println!("{:?}", v);
[2, 3]
fn main() {
let v = vec![1, 2, 3];
let v = v
.into_iter()
.map(|x| x + 1)
.filter(|&x| x < 4)
.collect::>();
println!("{:?}", v);
}
Итератори
Адаптери
let v = vec![1, 2, 3];
let v = v
.into_iter()
.map(|x| x + 1)
.filter(|&x| x < 4)
.collect::<Vec<_>>();
println!("{:?}", v);
[2, 3]
fn main() {
let v = vec![1, 2, 3];
let v = v
.into_iter()
.map(|x| x + 1)
.filter(|&x| x < 4)
.collect::>();
println!("{:?}", v);
}
Това е възможно, защото collect извиква FromIterator::from_iter.
Итератори
Има някои интересни имплементации на FromIterator.
Например връщане на единичен резултат от итеаратор с резултати:
fn batch(v: Vec<Result<u32, u32>>) -> Result<Vec<u32>, u32> {
v.into_iter().collect()
}
fn only_err(v: Vec<Result<(), u32>>) -> Result<(), u32> {
v.into_iter().collect()
}
println!("{:?}", batch(vec![Ok(1), Ok(2), Ok(3)]));
println!("{:?}", batch(vec![Ok(1), Err(2), Err(3)]));
println!();
println!("{:?}", only_err(vec![Ok(()), Ok(()), Ok(())]));
println!("{:?}", only_err(vec![Ok(()), Err(1), Err(2)]));
Ok([1, 2, 3]) Err(2) Ok(()) Err(1)
fn batch(v: Vec>) -> Result , u32> { v.into_iter().collect() } fn only_err(v: Vec >) -> Result<(), u32> { v.into_iter().collect() } fn main() { println!("{:?}", batch(vec![Ok(1), Ok(2), Ok(3)])); println!("{:?}", batch(vec![Ok(1), Err(2), Err(3)])); println!(); println!("{:?}", only_err(vec![Ok(()), Ok(()), Ok(())])); println!("{:?}", only_err(vec![Ok(()), Err(1), Err(2)])); }
Итератори
Адаптери
Преди да правите сложни цикли за трансформация, разгледайте Iterator, FromIterator и IntoIterator.
Closures

Closures
syntax
|x: u32| -> u32 { x + 1 }
|x| { x + 1 }
|x| x + 1
Closure vs fn
Каква е разликата между функция и closure?
Closures могат да използват променливи дефинирани по-горе в scope-a.
fn main() {
let other = String::from("foo"); // <-+
// |
Some("bar").map(|s| s.len() + other.len()); // --+
}
fn main() {
let other = String::from("foo"); // <-+
// |
Some("bar").map(|s| s.len() + other.len()); // --+
}
Вътрешна имплементация
Зад кулисите компилаторът създава една структура и една функция
/// Структура в която запомняме променливите, които сме прихванали
struct State {
other: String,
}
impl State {
/// Функция която съдържа логиката
fn call(&self, s: &str) -> usize {
s.len() + self.other.len()
}
}
#![allow(dead_code)]
fn main() {}
/// Структура в която запомняме променливите, които сме прихванали
struct State {
other: String,
}
impl State {
/// Функция която съдържа логиката
fn call(&self, s: &str) -> usize {
s.len() + self.other.len()
}
}
Вътрешна имплементация
Как бихме използвали нашата имплементация
fn map_option(opt: Option<&str>, f: State) -> Option<usize> {
match opt {
Some(s) => Some(f.call(s)),
None => None,
}
}
fn main() {
let other = String::from("foo");
map_option(Some("bar"), State { other });
}
#![allow(dead_code)]
struct State {
other: String,
}
impl State {
fn call(&self, s: &str) -> usize {
s.len() + self.other.len()
}
}
fn map_option(opt: Option<&str>, f: State) -> Option {
match opt {
Some(s) => Some(f.call(s)),
None => None,
}
}
fn main() {
let other = String::from("foo");
map_option(Some("bar"), State { other });
}
Move closure
Closure-ите, за разлика от нашата имплементация, не консумират прихванатите променливи по подразбиране
let other = String::from("foo");
println!("{:?}", Some("bar").map(|s| s.len() + other.len()));
println!("{:?}", other); // Ок
println!("{:?}", map_option(Some("bar"), State { other }));
// println!("{:?}", other); // комп. грешка - use of moved value `other`
Some(6) "foo" Some(6)
#![allow(dead_code)]
struct State {
other: String,
}
impl State {
fn call(&self, s: &str) -> usize {
s.len() + self.other.len()
}
}
fn map_option(opt: Option<&str>, f: State) -> Option {
match opt {
Some(s) => Some(f.call(s)),
None => None,
}
}
fn main() {
let other = String::from("foo");
println!("{:?}", Some("bar").map(|s| s.len() + other.len()));
println!("{:?}", other); // Ок
println!("{:?}", map_option(Some("bar"), State { other }));
// println!("{:?}", other); // комп. грешка - use of moved value `other`
}
Move closure
Можем да променим семантиката с ключовата дума move
|s| s.len() + other.len();
// генерира
struct State<'a> {
other: &'a String
}
move |s| s.len() + other.len();
// генерира
struct State {
other: String
}
Move closure
move премества стойността, независимо как се използва
let nums = vec![0, 1, 2, 3];
// прихваща `nums` като `Vec<i32>`
let f = move || {
for n in &nums {
println!("{}", n);
}
};
fn main() {
let nums = vec![0, 1, 2, 3];
// прихваща `nums` като `Vec`
let f = move || {
for n in &nums {
println!("{}", n);
}
};
}
Move closure
Ако искаме да преместим някоя стойност, но да прихванем друга по референция:
let nums = vec![0, 1, 2, 3];
let s = String::from("cookies");
let f = || {
// move `nums`
let nums = nums;
println!("{:?}", nums);
println!("{:?}", s);
};
println!("{:?}", s);
"cookies"
fn main() {
let nums = vec![0, 1, 2, 3];
let s = String::from("cookies");
let f = || {
// move `nums`
let nums = nums;
println!("{:?}", nums);
println!("{:?}", s);
};
println!("{:?}", s);
}
let nums = vec![0, 1, 2, 3];
let s = String::from("cookies");
let f = || {
// move `nums`
let nums = nums;
println!("{:?}", nums);
println!("{:?}", s);
};
println!("{:?}", nums);
error[E0382]: borrow of moved value: `nums` --> src/bin/main_81dc119f3e90ee303c7ebd23dc742d4904615a0d.rs:13:18 | 2 | let nums = vec![0, 1, 2, 3]; | ---- move occurs because `nums` has type `Vec<i32>`, which does not implement the `Copy` trait ... 5 | let f = || { | -- value moved into closure here 6 | // move `nums` 7 | let nums = nums; | ---- variable moved due to use in closure ... 13 | println!("{:?}", nums); | ^^^^ value borrowed here after move | = 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` due to previous error
fn main() {
let nums = vec![0, 1, 2, 3];
let s = String::from("cookies");
let f = || {
// move `nums`
let nums = nums;
println!("{:?}", nums);
println!("{:?}", s);
};
println!("{:?}", nums);
}Move closure
Или с move closure:
let nums = vec![0, 1, 2, 3];
let s = &String::from("cookies");
let f = move || {
println!("{:?}", nums);
println!("{:?}", s);
};
println!("{:?}", s);
"cookies"
fn main() {
let nums = vec![0, 1, 2, 3];
let s = &String::from("cookies");
let f = move || {
println!("{:?}", nums);
println!("{:?}", s);
};
println!("{:?}", s);
}
let nums = vec![0, 1, 2, 3];
let s = &String::from("cookies");
let f = move || {
println!("{:?}", nums);
println!("{:?}", s);
};
println!("{:?}", nums);
error[E0382]: borrow of moved value: `nums` --> src/bin/main_b7a8fdf6144b10d7b7ab47e78a1d1fd812793364.rs:10:18 | 2 | let nums = vec![0, 1, 2, 3]; | ---- move occurs because `nums` has type `Vec<i32>`, which does not implement the `Copy` trait ... 5 | let f = move || { | ------- value moved into closure here 6 | println!("{:?}", nums); | ---- variable moved due to use in closure ... 10 | println!("{:?}", nums); | ^^^^ value borrowed here after move | = 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` due to previous error
fn main() {
let nums = vec![0, 1, 2, 3];
let s = &String::from("cookies");
let f = move || {
println!("{:?}", nums);
println!("{:?}", s);
};
println!("{:?}", nums);
}Disjoint capture
Ако имаме някакви композитни данни като структура или кортеж, тогава Rust позволява да се прихванат само част от данните.
let x = (vec![0], vec![0]);
let c = move || {
// Само `x.0` се прихваща
println!("{:?}", x.0);
};
#![allow(unused_variables)]
fn main() {
let x = (vec![0], vec![0]);
let c = move || {
// Само `x.0` се прихваща
println!("{:?}", x.0);
};
}
Disjoint capture
Може да накараме компилатора да прихване цялата стойност с присвояване.
let x = (vec![0], vec![0]);
let c = move || {
let _ = &x;
println!("{:?}", x.0);
};
#![allow(unused_variables)]
fn main() {
let x = (vec![0], vec![0]);
let c = move || {
let _ = &x;
println!("{:?}", x.0);
};
}
Drop order
{
let x = (vec![0], vec![0]);
{
let c = || {
// Rust 2018 прихваща цялата `x`
// Rust 2021 прихваща само `x.0`
move_value(x.0);
};
// В Rust 2018 `c` и `x` се дропват на края на блока.
// В Rust 2021 `c` и `x.0` се дропват на края на блока.
}
// В Rust 2018 стойността на `x` е преместена, затова не се дропва тук.
// В Rust 2021 стойността на `x.0` е била дропната,
// но стойносттна на `x.1` се дропва тук.
}
#![allow(unused_variables)] fn move_value(val: T) {} fn main() { { let x = (vec![0], vec![0]); { let c = || { // Rust 2018 прихваща цялата `x` // Rust 2021 прихваща само `x.0` move_value(x.0); }; // В Rust 2018 `c` и `x` се дропват на края на блока. // В Rust 2021 `c` и `x.0` се дропват на края на блока. } // В Rust 2018 стойността на `x` е преместена, затова не се дропва тук. // В Rust 2021 стойността на `x.0` е била дропната, // но стойносттна на `x.1` се дропва тук. } }
Fn traits
FnFnMutFnOnce
Fn traits
FnFnMutFnOnce
Имат специален синтаксис, например
Fn()FnMut(u32, u32) -> boolFnOnce() -> String
Fn traits
FnOnce
// опростено
trait FnOnce<Args> {
type Output;
fn call_once(self, args: Args) -> Self::Output;
}
Fn traits
FnOnce
// опростено
trait FnOnce<Args> {
type Output;
fn call_once(self, args: Args) -> Self::Output;
}
self
Fn traits
FnOnce
// опростено
trait FnOnce<Args> {
type Output;
fn call_once(self, args: Args) -> Self::Output;
}
self- може да се извика само веднъж
Fn traits
FnOnce
// опростено
trait FnOnce<Args> {
type Output;
fn call_once(self, args: Args) -> Self::Output;
}
self- може да се извика само веднъж
- всички closure-и имплементират този трейт, защото могат да се извикат
Fn traits
FnOnce
// опростено
trait FnOnce<Args> {
type Output;
fn call_once(self, args: Args) -> Self::Output;
}
self- може да се извика само веднъж
- всички closure-и имплементират този трейт, защото могат да се извикат
- closure, който мести прихванати стойност извън тялото си, имплементира само
FnOnce
Fn traits
FnOnce
// опростено
trait FnOnce<Args> {
type Output;
fn call_once(self, args: Args) -> Self::Output;
}
self- може да се извика само веднъж
- всички closure-и имплементират този трейт, защото могат да се извикат
- closure, който мести прихванати стойност извън тялото си, имплементира само
FnOnce - в комбинация с move closure може да се използва за прехвърляне на собственост
Fn traits
FnMut
// опростено
trait FnMut<Args>: FnOnce<Args> {
fn call_mut(&mut self, args: Args) -> Self::Output;
}
Fn traits
FnMut
// опростено
trait FnMut<Args>: FnOnce<Args> {
fn call_mut(&mut self, args: Args) -> Self::Output;
}
&mut self
Fn traits
FnMut
// опростено
trait FnMut<Args>: FnOnce<Args> {
fn call_mut(&mut self, args: Args) -> Self::Output;
}
&mut self- може да се вика множество пъти и да се променят прихванатите стойност
Fn traits
Fn
// опростено
trait Fn<Args>: FnMut<Args> {
fn call(&self, args: Args) -> Self::Output;
}
Fn traits
Fn
// опростено
trait Fn<Args>: FnMut<Args> {
fn call(&self, args: Args) -> Self::Output;
}
&self
Fn traits
Fn
// опростено
trait Fn<Args>: FnMut<Args> {
fn call(&self, args: Args) -> Self::Output;
}
&self- може да се вика множество пъти, но не могат да се променят прихванатите стойност
Fn traits
Fn
// опростено
trait Fn<Args>: FnMut<Args> {
fn call(&self, args: Args) -> Self::Output;
}
&self- може да се вика множество пъти, но не могат да се променят прихванатите стойност
- това е важно качество в среди, където clusure-a се вика множество пъти конкурентно
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- (ownership):
FnOnce
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- (ownership):
FnOnce - (
&mut):FnOnce+FnMut
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- (ownership):
FnOnce - (
&mut):FnOnce+FnMut - (
&):FnOnce+FnMut+Fn
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- (ownership):
FnOnce - (
&mut):FnOnce+FnMut - (
&):FnOnce+FnMut+Fn - Не можем да имаме само
FnилиFnMutзаради ограниченията на трейтовете
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- (ownership):
FnOnce - (
&mut):FnOnce+FnMut - (
&):FnOnce+FnMut+Fn - Не можем да имаме само
FnилиFnMutзаради ограниченията на трейтовете - Ако това ви се струва странно, може да го мислите като ограниченията при взимане на референция
let x = ...→&mut x→&x
Taking closures
По-популярния начин за взимане на closure е чрез static dispatch
fn eval_and_increment<F>(f: F) -> usize where F: Fn???() -> usize {
f() + 1
}
println!("{}", eval_and_increment(|| 1));
Taking closures
Кой Fn trait да сложим за ограничение?
| Тип | Прихващане на стойности | Брой викания | Кога? |
|---|---|---|---|
FnOnce |
може да местим, променяме и четем стойности | веднъж | move closures |
FnMut |
може да променяме и четем стойности | множество пъти | викане множество пъти |
Fn |
може да четем стойности | множество пъти | когато не можем да прихванем &mut референция |
Taking closures
Кой Fn trait да сложим за ограничение?
| Тип | Прихващане на стойности | Брой викания | Кога? |
|---|---|---|---|
FnOnce |
може да местим, променяме и четем стойности | веднъж | move closures |
FnMut |
може да променяме и четем стойности | множество пъти | викане множество пъти |
Fn |
може да четем стойности | множество пъти | когато не можем да прихванем &mut референция |
или пробваме в този ред докато компилаторът ни разреши 😉
Taking closures
fn eval_and_increment<F>(f: F) -> usize where F: FnOnce() -> usize {
f() + 1
}
println!("{}", eval_and_increment(|| 1));
2
fn eval_and_increment(f: F) -> usize where F: FnOnce() -> usize { f() + 1 } fn main() { println!("{}", eval_and_increment(|| 1)); }
Taking closures
Ако closure не прихваща нищо, той се свежда до функция
fn eval_and_increment(f: fn() -> usize) -> usize {
f() + 1
}
println!("{}", eval_and_increment(|| 1));
2
fn eval_and_increment(f: fn() -> usize) -> usize {
f() + 1
}
fn main() {
println!("{}", eval_and_increment(|| 1));
}
Taking closures
Може да подаваме функции, където се очаква closure
fn one() -> usize {
1
}
fn eval_and_increment<F>(f: F) -> usize where F: FnOnce() -> usize {
f() + 1
}
println!("{}", eval_and_increment(one));
2
fn one() -> usize {
1
}
fn eval_and_increment(f: F) -> usize where F: FnOnce() -> usize {
f() + 1
}
fn main() {
println!("{}", eval_and_increment(one));
}
Returning closures
impl Trait
Не знаем типа на closure-a тъй като се генерира при компилиране, съответно това е един начин за връщане на closure
fn curry(a: u32) -> impl Fn(u32) -> u32 {
move |b| a + b
}
println!("{}", curry(1)(2));
3
fn curry(a: u32) -> impl Fn(u32) -> u32 {
move |b| a + b
}
fn main() {
println!("{}", curry(1)(2));
}
impl Trait
Може да стои на мястото на типа на аргумент или return типа
use std::fmt::Debug;
fn id(arg: impl Debug) -> impl Debug {
arg
}
println!("{:?}", id(1));
println!("{:?}", id("foo"));
1 "foo"
use std::fmt::Debug;
fn id(arg: impl Debug) -> impl Debug {
arg
}
fn main() {
println!("{:?}", id(1));
println!("{:?}", id("foo"));
}
impl Trait
Не може да правите нищо друго с него освен това което имплементира
use std::fmt::Debug;
fn id(arg: impl Debug) -> impl Debug {
arg
}
println!("{}", id(1));
error[E0277]: `impl Debug` doesn't implement `std::fmt::Display` --> src/bin/main_8bf7039ea5b999af5f79cbab4e653a034da9476d.rs:7:16 | 7 | println!("{}", id(1)); | ^^^^^ `impl Debug` cannot be formatted with the default formatter | = help: the trait `std::fmt::Display` is not implemented for `impl Debug` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = 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 E0277`. error: could not compile `rust` due to previous error
use std::fmt::Debug;
fn id(arg: impl Debug) -> impl Debug {
arg
}
fn main() {
println!("{}", id(1));
}
impl Trait
Удобно е за връщане на итератори
use std::fmt::Debug;
fn create_iter() -> impl Iterator<Item = impl Debug> {
vec![0, 1, 2].into_iter().map(|x| x * 2).enumerate()
}
fn main() {
for x in create_iter() {
println!("{:?}", x);
}
}
(0, 0) (1, 2) (2, 4)
use std::fmt::Debug; fn create_iter() -> impl Iterator- { vec![0, 1, 2].into_iter().map(|x| x * 2).enumerate() } fn main() { for x in create_iter() { println!("{:?}", x); } }
impl Trait
Разликата между generics и impl Trait
- generics поддържа turbofish синтаксис и изисква да се пише задължително при функция като
f<T>() -> T - impl Trait оставя компилатора да оправи типа включително и като return тип, но не може да използваме turbofish при извикване на функцията