Често срещани типажи, итератори и анонимни функции
9 ноември 2020
Често използвани типажи
Често използвани типажи
Стандартната библиотека дефинира често използвани типажи
Често използвани типажи
Стандартната библиотека дефинира често използвани типажи
Голяма част от 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
- Използва се от типове и функции, които използват хеширане
- Например ключовете на
HashMapиHashSet
Hash
- Използва се от типове и функции, които използват хеширане
- Например ключовете на
HashMapиHashSet - Може да се добави с
#[derive(Hash)], ако всички полета имплементиратHash
Hash
- Използва се от типове и функции, които използват хеширане
- Например ключовете на
HashMapиHashSet - Може да се добави с
#[derive(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.
Итератори
Адаптери
Преди да правите някакви странни цикли за трансформация, разгледайте какви адаптери има за Iterator.
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 `std::vec::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
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 `std::vec::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
fn main() {
let nums = vec![0, 1, 2, 3];
let s = &String::from("cookies");
let f = move || {
println!("{:?}", nums);
println!("{:?}", s);
};
println!("{:?}", nums);
}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- може да се извика само веднъж
- в комбинация с 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
Когато създадем closure, компилатора имплементира всички trait-ове, които може
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- (
&):FnOnce+FnMut+Fn
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- (
&):FnOnce+FnMut+Fn - (
&mut):FnOnce+FnMut
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- (
&):FnOnce+FnMut+Fn - (
&mut):FnOnce+FnMut - (ownership):
FnOnce
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- (
&):FnOnce+FnMut+Fn - (
&mut):FnOnce+FnMut - (ownership):
FnOnce - Не можем да имаме само
FnилиFnMutзаради ограниченията на трейтовете
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- (
&):FnOnce+FnMut+Fn - (
&mut):FnOnce+FnMut - (ownership):
FnOnce - Не можем да имаме само
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)); }
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 std::fmt::Debug` doesn't implement `std::fmt::Display` --> src/bin/main_8bf7039ea5b999af5f79cbab4e653a034da9476d.rs:7:16 | 7 | println!("{}", id(1)); | ^^^^^ `impl std::fmt::Debug` cannot be formatted with the default formatter | = help: the trait `std::fmt::Display` is not implemented for `impl std::fmt::Debug` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: required by `std::fmt::Display::fmt` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
use std::fmt::Debug;
fn id(arg: impl Debug) -> impl Debug {
arg
}
fn main() {
println!("{}", id(1));
}
impl Trait
Разликата между generics и impl Trait
- generics поддържа turbofish синтаксис и изисква да се пише задължително при функция като
f<T>() -> T - impl Trait оставя компилатора да оправи типа включително и като return тип, но не може да използваме turbofish при извикване на функцията