Често срещани типажи. Анонимни функции
7 ноември 2024
Преговор
- lifetime анотации -
&'a T,&'a mut T
Преговор
- lifetime анотации -
&'a T,&'a mut T - lifetimes в дефиниции на функции
Преговор
- lifetime анотации -
&'a T,&'a mut T - lifetimes в дефиниции на функции
- оказват с кои аргументи е свързан резултата
fn map_get<'a>(map: &'a HashMap<String, String>, key: &str) -> &'a str
Преговор
- lifetime анотации -
&'a T,&'a mut T - lifetimes в дефиниции на функции
- оказват с кои аргументи е свързан резултата
fn map_get<'a>(map: &'a HashMap<String, String>, key: &str) -> &'a str
- lifetimes в структури
Преговор
- lifetime анотации -
&'a T,&'a mut T - lifetimes в дефиниции на функции
- оказват с кои аргументи е свързан резултата
fn map_get<'a>(map: &'a HashMap<String, String>, key: &str) -> &'a str
- lifetimes в структури
- структурата има ограничен живот, защото съдържа референции
struct Words<'a> { text: &'a str }
Често използвани типажи
Често използвани типажи
- стандартната библиотека дефинира често използвани типажи
Често използвани типажи
- стандартната библиотека дефинира често използвани типажи
- голяма част от Rust екосистемата разчита на тях
Често използвани типажи
- стандартната библиотека дефинира често използвани типажи
- голяма част от Rust екосистемата разчита на тях
- само ние можем да имплементираме стандартните trait-ове за наши типове
Често използвани типажи
- стандартната библиотека дефинира често използвани типажи
- голяма част от Rust екосистемата разчита на тях
- само ние можем да имплементираме стандартните trait-ове за наши типове
- ако пишем библиотека - добре е да имплементираме всички стандартни trait-ове, които можем
Често използвани типажи
Списък
- Copy
- Clone
- Eq
- PartialEq
- Ord
- PartialOrd
- Hash
- Debug
- 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-ване
Drop
Имплементацията на std::mem::drop
pub fn drop<T>(_x: T) {}
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 Defaultилиfn new() -> Self?
Default
trait Default {
fn default() -> Self;
}
- Позволява създаване на обект със стойност по подразбиране
- Може да се добави с
#[derive(Default)], ако всички полета имплементиратDefault Defaultилиfn new() -> Self?- може и двете
Defaultе интерфейс - позволява използването на типа в generic кодnewе по-очаквано от програмиста
Hash
Hash
- Използва се от типове и функции, които използват хеширане
Hash
Hash
Display
Display
- Използва се от placeholder-a
{}за форматиране на стойност, която ще се показва на потребителя
Display
- Използва се от placeholder-a
{}за форматиране на стойност, която ще се показва на потребителя - Не може да се derive-не за разлика от
Debug
Display
use std::fmt::{self, Display, Formatter};
struct MagicTrick {
description: String,
secrets: Vec<String>,
skills: Vec<String>,
}
impl Display for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Магически трик {:?}", self.description)
}
}
fn main() {}
use std::fmt::{self, Display, Formatter};
struct MagicTrick {
description: String,
secrets: Vec,
skills: Vec,
}
impl Display for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Магически трик {:?}", self.description)
}
}
Display
Макрос write
write!(f, "Магически трик {:?}", self.description)
Display
Макрос write
write!(f, "Магически трик {:?}", self.description)
- Подобно на
print!иformat!
Display
Макрос write
write!(f, "Магически трик {:?}", self.description)
- Подобно на
print!иformat! - Записва форматиран текст в структура, която имплементира
std::fmt::Writeилиstd::io::Write
Display
Макрос write
write!(f, "Магически трик {:?}", self.description)
- Подобно на
print!иformat! - Записва форматиран текст в структура, която имплементира
std::fmt::Writeилиstd::io::Write
// разгръща се до
f.write_fmt(format_args!("Магически трик {:?}", self.description))
- всички подобни макроси (
print!,write!,format!) се свеждат доformat_args! format_args!е вградено в компилатора и не се разгръща до Rust код
Debug
- placeholder
{:?}
Debug
- placeholder
{:?} - форматиране на стойност, която ще се показва само с цел дебъгване
Debug
- placeholder
{:?} - форматиране на стойност, която ще се показва само с цел дебъгване
#[derive(Debug)]имплементира версия по подразбиране
Debug
- placeholder
{:?} - форматиране на стойност, която ще се показва само с цел дебъгване
#[derive(Debug)]имплементира версия по подразбиране
#[derive(Debug)]
struct MagicTrick {
description: String,
secrets: Vec<String>,
skills: Vec<String>,
}
fn main() {
let trick = MagicTrick {
description: String::from("Изчезваща монета"),
secrets: vec![String::from("Монетата се прибира в ръкава")],
skills: vec![String::from("Бързи ръце"), String::from("Заблуда")],
};
println!("{:?}", trick);
}
MagicTrick { description: "Изчезваща монета", secrets: ["Монетата се прибира в ръкава"], skills: ["Бързи ръце", "Заблуда"] }
#[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);
}
Debug
Formatter структура
- може да напишем и собствена имплементация
std::fmt::Formatterсъдържа набор от полезни функции -pad,precision,width, и други
Debug
Formatter структура
- може да напишем и собствена имплементация
std::fmt::Formatterсъдържа набор от полезни функции -pad,precision,width, и други
use std::fmt::{self, Debug, Formatter};
impl Debug for MagicTrick {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("Трик")
.field("описание", &self.description)
.field("тайни", &self.secrets)
.field("умения", &self.skills)
.finish()
}
}
fn main() {
let trick = MagicTrick { /* ... */ };
println!("{:?}", trick);
}
Трик { описание: "Изчезваща монета", тайни: ["Монетата се прибира в ръкава"], умения: ["Бързи ръце", "Заблуда"] }
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 {
f.debug_struct("Трик")
.field("описание", &self.description)
.field("тайни", &self.secrets)
.field("умения", &self.skills)
.finish()
}
}
fn main() {
/*
let trick = MagicTrick { /* ... */ };
*/
let trick = MagicTrick {
description: String::from("Изчезваща монета"),
secrets: vec![String::from("Монетата се прибира в ръкава")],
skills: vec![String::from("Бързи ръце"), String::from("Заблуда")],
};
println!("{:?}", trick);
}
Предефиниране на оператори
Операторите се дефинират с trait-ове
Видяхме trait-а Add, с който дефинираме оператора +
trait Add<RHS = Self> {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
Предефиниране на оператори
Примери
- Add, Sub, Mul, Div, Rem
- BitAnd, BitOr, BitXor, Shl, Shr
- *Assign (AddAssign, SubAssign, и т.н.)
- Neg, Not
- Index
- IndexMut
Предефиниране на оператори
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
assert_eq!(f64::NAN == f64::NAN, false);
Предефиниране на оператори
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!(f64::NAN == f64::NAN, false);
warning: incorrect NaN comparison, NaN cannot be directly compared to itself --> src/bin/main_67b4f7e90fa6410e6b5325ec4fb8eb835bf62969.rs:2:16 | 2 | assert_eq!(f64::NAN == f64::NAN, false); | ^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(invalid_nan_comparisons)]` on by default help: use `f32::is_nan()` or `f64::is_nan()` instead | 2 - assert_eq!(f64::NAN == f64::NAN, false); 2 + assert_eq!(f64::NAN.is_nan(), false); |
fn main() {
assert_eq!(f64::NAN == 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
Предефиниране на оператори
Eq
trait Eq: PartialEq<Self> { }
- Marker trait
- Задължава
a == aда еtrue
trait Eq: PartialEq<Self> означава, че Eq e subtrait на PartialEq<Self>.
За да имплементираме Eq за T, трябва да има имплементация на PartialEq<T> за T.
Предефиниране на оператори
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!(f64::NAN < 0.0, false);
assert_eq!(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 е изпълнено.
Closures

Closures
синтаксис
|x: u32| -> u32 { x + 1 }
|x| { x + 1 }
|x| x + 1
Closures
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()
}
}
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 });
}
Вътрешна имплементация
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`
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`
}
Вътрешна имплементация
Всъщност имплементацията изглежда така
struct State<'a> {
other: &'a String,
}
impl<'a> State<'a> {
fn call(&self, s: &str) -> usize {
s.len() + self.other.len()
}
}
fn main() {}
struct State<'a> {
other: &'a String,
}
impl<'a> State<'a> {
fn call(&self, s: &str) -> usize {
s.len() + self.other.len()
}
}
Вътрешна имплементация
Всъщност и това не е вярно.
Ако променливата се използва зад референция, ще я прихване по референция
let nums = vec![1, 2, 3];
let f = || {
for n in nums.iter() { // Vec::iter(&self)
println!("{}", n); // ==> прихваща nums като &Vec<i32>
}
};
f();
println!("{:?}", nums);
1 2 3 [1, 2, 3]
fn main() {
let nums = vec![1, 2, 3];
let f = || {
for n in nums.iter() { // Vec::iter(&self)
println!("{}", n); // ==> прихваща nums като &Vec
}
};
f();
println!("{:?}", nums);
}
Вътрешна имплементация
..
Ако променливата се използва по стойност, ще се премести в анонимната функция
let nums = vec![1, 2, 3];
let f = || {
for n in nums.into_iter() { // Vec::into_iter(self)
println!("{}", n); // ==> прихваща nums като Vec<i32>
}
};
f();
// println!("{:?}", nums);
1 2 3
fn main() {
let nums = vec![1, 2, 3];
let f = || {
for n in nums.into_iter() { // Vec::into_iter(self)
println!("{}", n); // ==> прихваща nums като Vec
}
};
f();
// println!("{:?}", nums);
}
Вътрешна имплементация
..
Ако променливата се използва по стойност, ще се премести в анонимната функция
let nums = vec![1, 2, 3];
let f = || {
for n in nums.into_iter() { // Vec::into_iter(self)
println!("{}", n); // ==> прихваща nums като Vec<i32>
}
};
f();
println!("{:?}", nums);
error[E0382]: borrow of moved value: `nums` --> src/bin/main_46965ba87cdce000f98685440ed7123d92fee21a.rs:11:18 | 2 | let nums = vec![1, 2, 3]; | ---- move occurs because `nums` has type `Vec<i32>`, which does not implement the `Copy` trait 3 | 4 | let f = || { | -- value moved into closure here 5 | for n in nums.into_iter() { // Vec::into_iter(self) | ---- variable moved due to use in closure ... 11 | 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) help: consider cloning the value if the performance cost is acceptable | 5 | for n in nums.clone().into_iter() { // Vec::into_iter(self) | ++++++++ For more information about this error, try `rustc --explain E0382`. error: could not compile `rust` (bin "main_46965ba87cdce000f98685440ed7123d92fee21a") due to 1 previous error
fn main() {
let nums = vec![1, 2, 3];
let f = || {
for n in nums.into_iter() { // Vec::into_iter(self)
println!("{}", n); // ==> прихваща nums като Vec
}
};
f();
println!("{:?}", nums);
}
Move closure
Можем да променим семантиката с ключовата дума move
let other = String::from("foo");
println!("{:?}", Some("bar").map(|s| s.len() + other.len()));
println!("{:?}", other); // Ок
println!("{:?}", Some("bar").map(move |s| s.len() + other.len()));
// println!("{:?}", other); // грешка: use of moved value: `other`
fn main() {
let other = String::from("foo");
println!("{:?}", Some("bar").map(|s| s.len() + other.len()));
println!("{:?}", other); // Ок
println!("{:?}", Some("bar").map(move |s| s.len() + other.len()));
// println!("{:?}", other); // грешка: use of moved value: `other`
}
Move closure
Можем да променим семантиката с ключовата дума move
let closure = |s| s.len() + other.len();
// генерира
struct State<'a> {
other: &'a String
}
let closure = move |s| s.len() + other.len();
// генерира
struct State {
other: String
}
Move closure
Ако искаме да преместим някоя стойност, но да прихванем друга по референция:
let nums = vec![0, 1, 2, 3];
let cookies = String::from("cookies");
let f = || {
// move `nums`
let nums = nums;
println!("{:?}", nums);
println!("{:?}", cookies);
};
// println!("{:?}", nums);
println!("{:?}", cookies);
"cookies"
fn main() {
let nums = vec![0, 1, 2, 3];
let cookies = String::from("cookies");
let f = || {
// move `nums`
let nums = nums;
println!("{:?}", nums);
println!("{:?}", cookies);
};
// println!("{:?}", nums);
println!("{:?}", cookies);
}
Move closure
Друг вариант, използвайки move closure
let nums = vec![0, 1, 2, 3];
let cookies = String::from("cookies");
let f = {
let cookies_ref = &cookies; // ще премести `cookies_ref` във функцията,
// но типа на `cookies_ref` е `&String`
move || {
println!("{:?}", nums);
println!("{:?}", cookies_ref);
}
};
// println!("{:?}", nums);
println!("{:?}", cookies);
"cookies"
fn main() {
let nums = vec![0, 1, 2, 3];
let cookies = String::from("cookies");
let f = {
let cookies_ref = &cookies; // ще премести `cookies_ref` във функцията,
// но типа на `cookies_ref` е `&String`
move || {
println!("{:?}", nums);
println!("{:?}", cookies_ref);
}
};
// println!("{:?}", nums);
println!("{:?}", cookies);
}
Move closure
Друг вариант, използвайки move closure
let nums = vec![0, 1, 2, 3];
let cookies = String::from("cookies");
let f = {
let cookies = &cookies; // ще премести `cookies` във функцията,
// но типа на `cookies` е `&String`
move || {
println!("{:?}", nums);
println!("{:?}", cookies);
}
};
// println!("{:?}", nums);
println!("{:?}", cookies);
"cookies"
fn main() {
let nums = vec![0, 1, 2, 3];
let cookies = String::from("cookies");
let f = {
let cookies = &cookies; // ще премести `cookies` във функцията,
// но типа на `cookies` е `&String`
move || {
println!("{:?}", nums);
println!("{:?}", cookies);
}
};
// println!("{:?}", nums);
println!("{:?}", cookies);
}
Closures като аргументи
Closures като аргументи
Как да подадем анонимна функция като аргумент?
fn map_option(opt: Option<&str>, f: ???) -> Option<u32> {
match opt {
Some(s) => Some(f(s)),
None => None,
}
}
Closures като аргументи
Как да подадем анонимна функция като аргумент?
fn map_option<F>(opt: Option<&str>, f: F) -> Option<u32>
where
F: FnOnce(&str) -> u32,
{
match opt {
Some(s) => Some(f(s)),
None => None,
}
}
fn main() {}
fn map_option(opt: Option<&str>, f: F) -> Option
where
F: FnOnce(&str) -> u32,
{
match opt {
Some(s) => Some(f(s)),
None => None,
}
}
Fn traits
FnFnMutFnOnce
Fn traits
FnFnMutFnOnce
Имат специален синтаксис, например
Fn()FnMut(u32, u32) -> boolFnOnce() -> String
Fn traits
trait FnOnce<Args> {
type Output;
fn call_once(self, args: Args) -> Self::Output;
}
trait FnMut<Args> : FnOnce<Args> {
fn call_mut(&mut self, args: Args) -> Self::Output;
}
trait Fn<Args> : FnMut<Args> {
fn call(&self, args: Args) -> Self::Output;
}
Fn traits
Fn
&self
Fn traits
Fn
&self- функцията се нуждае само от споделена референция към прихванатото състояние
Fn traits
Fn
&self- функцията се нуждае само от споделена референция към прихванатото състояние
- може да чете от състоянието, но не може да го модифицира
Fn traits
FnMut
&mut self
Fn traits
FnMut
&mut self- функцията се нуждае от ексклузивна референция към прихванатото състояние
Fn traits
FnMut
&mut self- функцията се нуждае от ексклузивна референция към прихванатото състояние
- може да променя състоянието
Fn traits
FnMut
&mut self- функцията се нуждае от ексклузивна референция към прихванатото състояние
- може да променя състоянието
- или да променя външни променливи, прихванати по
&mut T
Fn traits
FnMut
Пример - функция, която си променя състоянието
let mut counter = 0;
let mut f = move || {
counter += 1;
counter
};
println!("{}", f());
println!("{}", f());
println!("{}", f());
1 2 3
fn main() {
let mut counter = 0;
let mut f = move || {
counter += 1;
counter
};
println!("{}", f());
println!("{}", f());
println!("{}", f());
}
Fn traits
FnMut
Пример - функция, която променя външна прихваната променлива
let mut buffer = String::new();
let mut f = |text| {
buffer.push_str(text);
buffer.len()
};
println!("{}", f("foo"));
println!("{}", f("bar"));
println!("{}", buffer);
3 6 foobar
fn main() {
let mut buffer = String::new();
let mut f = |text| {
buffer.push_str(text);
buffer.len()
};
println!("{}", f("foo"));
println!("{}", f("bar"));
println!("{}", buffer);
}
Fn traits
FnOnce
self
Fn traits
FnOnce
self- функцията консумира прихванатото състояние
Fn traits
FnOnce
self- функцията консумира прихванатото състояние
- може да се извика само веднъж
Fn traits
FnOnce
Пример
let nums = vec![1, 2, 3];
let f = || {
for n in nums.into_iter() {
println!("{}", n);
}
};
f();
// f();
1 2 3
fn main() {
let nums = vec![1, 2, 3];
let f = || {
for n in nums.into_iter() {
println!("{}", n);
}
};
f();
// f();
}
Fn traits
FnOnce
Пример
let nums = vec![1, 2, 3];
let f = || {
for n in nums.into_iter() {
println!("{}", n);
}
};
f();
f();
error[E0382]: use of moved value: `f` --> src/bin/main_faa575e42fe03f0ba26536c5ae8d02ab15c81572.rs:11:1 | 10 | f(); | --- `f` moved due to this call 11 | f(); | ^ value used here after move | note: closure cannot be invoked more than once because it moves the variable `nums` out of its environment --> src/bin/main_faa575e42fe03f0ba26536c5ae8d02ab15c81572.rs:5:14 | 5 | for n in nums.into_iter() { | ^^^^ note: this value implements `FnOnce`, which causes it to be moved when called --> src/bin/main_faa575e42fe03f0ba26536c5ae8d02ab15c81572.rs:10:1 | 10 | f(); | ^ For more information about this error, try `rustc --explain E0382`. error: could not compile `rust` (bin "main_faa575e42fe03f0ba26536c5ae8d02ab15c81572") due to 1 previous error
fn main() {
let nums = vec![1, 2, 3];
let f = || {
for n in nums.into_iter() {
println!("{}", n);
}
};
f();
f();
}
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 traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- изисква ownership --
FnOnce - изисква
&mut--FnOnce+FnMut - изисква
&--FnOnce+FnMut+Fn
Защо?
- имаме
state-- можем да извикамеcall(&state),call_mut(&mut state)илиcall_once(state)
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- изисква ownership --
FnOnce - изисква
&mut--FnOnce+FnMut - изисква
&--FnOnce+FnMut+Fn
Защо?
- имаме
state-- можем да извикамеcall(&state),call_mut(&mut state)илиcall_once(state) - имаме
&mut state-- можем да извикамеcall(&state),call_mut(&mut state)
Fn traits
Когато създадем closure, компилатора имплементира всички trait-ове, които може
- изисква ownership --
FnOnce - изисква
&mut--FnOnce+FnMut - изисква
&--FnOnce+FnMut+Fn
Защо?
- имаме
state-- можем да извикамеcall(&state),call_mut(&mut state)илиcall_once(state) - имаме
&mut state-- можем да извикамеcall(&state),call_mut(&mut state) - имаме
&state-- можем да извикаме самоcall(&state)
Fn traits
Кои трейтове да използваме?
Fn traits
Кои трейтове да използваме?
Ако приемаме closure като аргумент:
FnOnce→FnMut→Fn
Fn traits
Кои трейтове да използваме?
Ако приемаме closure като аргумент:
FnOnce→FnMut→Fn
Ако връщаме closure:
Fn→FnMut→FnOnce
fn и Fn
fnе указател към функция (function pointer)- функция без състояние
fn и Fn
fnе указател към функция (function pointer)- функция без състояние
fn repeat(c: char, count: u32) -> String { /*...*/ }
error[E0601]: `main` function not found in crate `main_732470937c391955b0bf4cfefbf788cc52fa92d9` --> src/bin/main_732470937c391955b0bf4cfefbf788cc52fa92d9.rs:1:53 | 1 | fn repeat(c: char, count: u32) -> String { /*...*/ } | ^ consider adding a `main` function to `src/bin/main_732470937c391955b0bf4cfefbf788cc52fa92d9.rs` error[E0308]: mismatched types --> src/bin/main_732470937c391955b0bf4cfefbf788cc52fa92d9.rs:1:35 | 1 | fn repeat(c: char, count: u32) -> String { /*...*/ } | ------ ^^^^^^ expected `String`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression Some errors have detailed explanations: E0308, E0601. For more information about an error, try `rustc --explain E0308`. error: could not compile `rust` (bin "main_732470937c391955b0bf4cfefbf788cc52fa92d9") due to 2 previous errors
fn repeat(c: char, count: u32) -> String { /*...*/ }
- типа на
repeatеfn(char, u32) -> String
fn и Fn
fn типовете имплементират всички Fn traits
- можем да подаваме нормални функции на места, където се очаква тип, имплементиращ някой
Fntrait
Closures, които не прихващат нищо, могат да се конвертират до fn тип
let add_one: fn(u32) -> u32 = |x| x + 1;
fn main() {
let add_one: fn(u32) -> u32 = |x| x + 1;
}
Closures като резултат
Как да върнем анонимна функция като резултат?
fn make_auto_incrementer() -> ??? {
let mut counter = 0;
move || {
counter += 1;
counter
}
}
Closures като резултат
Конкретен тип?
- няма как да назовем автоматично генерирания тип за closure-а
fn make_auto_incrementer() -> ??? {
let mut counter = 0;
move || {
counter += 1;
counter
}
}
Closures като резултат
Generic тип?
- типа се избира от този, който извиква функцията
- това би значило
- извикващия си измисля произволен тип
- ние създаваме и връщаме стойност от този тип -- няма как
fn make_auto_incrementer<F: FnMut() -> u32>() -> F {
let mut counter = 0;
move || {
counter += 1;
counter
}
}
error[E0308]: mismatched types --> src/bin/main_b611835db2c98592b33556b63109ccc642e8c41c.rs:5:5 | 3 | fn make_auto_incrementer<F: FnMut() -> u32>() -> F { | - - | | | | | expected `F` because of return type | | help: consider using an impl return type: `impl FnMut() -> u32` | expected this type parameter 4 | let mut counter = 0; 5 | / move || { 6 | | counter += 1; 7 | | counter 8 | | } | |_____^ expected type parameter `F`, found closure | = note: expected type parameter `F` found closure `{closure@src/bin/main_b611835db2c98592b33556b63109ccc642e8c41c.rs:5:5: 5:12}` = help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `F` For more information about this error, try `rustc --explain E0308`. error: could not compile `rust` (bin "main_b611835db2c98592b33556b63109ccc642e8c41c") due to 1 previous error
fn main() {}
fn make_auto_incrementer u32>() -> F {
let mut counter = 0;
move || {
counter += 1;
counter
}
}
Closures като резултат
Trait object
fn make_auto_incrementer() -> Box<dyn FnMut() -> u32> {
let mut counter = 0;
Box::new(move || {
counter += 1;
counter
})
}
fn make_auto_incrementer() -> Boxu32> { let mut counter = 0; Box::new(move || { counter += 1; counter }) } fn main() {}
Closures като резултат
Trait object
fn make_auto_incrementer() -> Box<dyn FnMut() -> u32> {
let mut counter = 0;
Box::new(move || {
counter += 1;
counter
})
}
fn main() {
let mut inc = make_auto_incrementer();
println!("{}", inc());
println!("{}", inc());
println!("{}", inc());
}
1 2 3
fn make_auto_incrementer() -> Boxu32> { let mut counter = 0; Box::new(move || { counter += 1; counter }) } fn main() { let mut inc = make_auto_incrementer(); println!("{}", inc()); println!("{}", inc()); println!("{}", inc()); }
Closures като резултат
impl Trait
fn make_auto_incrementer() -> impl FnMut() -> u32 {
let mut counter = 0;
move || {
counter += 1;
counter
}
}
fn make_auto_incrementer() -> impl FnMut() -> u32 {
let mut counter = 0;
move || {
counter += 1;
counter
}
}
fn main() {}
Closures като резултат
impl Trait
fn make_auto_incrementer() -> impl FnMut() -> u32 {
let mut counter = 0;
move || {
counter += 1;
counter
}
}
fn main() {
let mut inc = make_auto_incrementer();
println!("{}", inc());
println!("{}", inc());
println!("{}", inc());
}
1 2 3
fn make_auto_incrementer() -> impl FnMut() -> u32 {
let mut counter = 0;
move || {
counter += 1;
counter
}
}
fn main() {
let mut inc = make_auto_incrementer();
println!("{}", inc());
println!("{}", inc());
println!("{}", inc());
}
impl Trait
като резултат
fn make_auto_incrementer() -> impl FnMut() -> u32 { /*...*/ }
impl Trait
като резултат
fn make_auto_incrementer() -> impl FnMut() -> u32 { /*...*/ }
- функцията връща конкретен тип
impl Trait
като резултат
fn make_auto_incrementer() -> impl FnMut() -> u32 { /*...*/ }
- функцията връща конкретен тип
- компилатора генерира анонимен тип спрямо тялото на функцията
impl Trait
като резултат
fn make_auto_incrementer() -> impl FnMut() -> u32 { /*...*/ }
- функцията връща конкретен тип
- компилатора генерира анонимен тип спрямо тялото на функцията
- за типа се знае единствено, че имплементира зададените trait-ове
- (в случая
FnMut() -> u32)
impl Trait
като резултат
Удобно за използване, когато:
impl Trait
като резултат
Удобно за използване, когато:
- не можем да назовем типа на резултата (напр. closure, impl Trait върнат от друга функция)
impl Trait
като резултат
Удобно за използване, когато:
- не можем да назовем типа на резултата (напр. closure, impl Trait върнат от друга функция)
- не изкаме да назовем типа, защото е много сложен (напр. вложени итератори и адаптери)
impl Trait
като резултат
Удобно за използване, когато:
- не можем да назовем типа на резултата (напр. closure, impl Trait върнат от друга функция)
- не изкаме да назовем типа, защото е много сложен (напр. вложени итератори и адаптери)
- искаме да скрием имплементационен детайл, но не можем да си позволим алокация за
Box<dyn Trait>
impl Trait
като аргумент
impl Trait може да се напише и в позицията на аргумент.
Тогава е (почти) еквивалнтен на generic параметър.
fn print1(t: impl Display) {
println!("{}", t);
}
fn print2<T: Display>(t: T) {
println!("{}", t);
}
fn main() {}
use std::fmt::Display;
fn print1(t: impl Display) {
println!("{}", t);
}
fn print2(t: T) {
println!("{}", t);
}
impl Trait
като аргумент
impl Trait може да се напише и в позицията на аргумент.
Тогава е (почти) еквивалнтен на generic параметър.
fn print1(t: impl Display) {
println!("{}", t);
}
fn print2<T: Display>(t: T) {
println!("{}", t);
}
fn main() {}
use std::fmt::Display;
fn print1(t: impl Display) {
println!("{}", t);
}
fn print2(t: T) {
println!("{}", t);
}
- разликата -- за
print2можем да ползваме turbofish -print2::<String>(s); - за
print1не можем