Замыкания

Замыкания в Rust - это анонимные функции, которые можно сохранять в переменных или передавать в качестве аргументов другим функциям. Вы можете создать замыкание в одном месте, а затем вызвать его в каком-нибудь другом, чтобы выполнить обработку в ином контексте. В отличие от функций, замыкания могут использовать значения из области видимости в которой они были определены.

Хорошая функция unwrap_or_else(|| self.most_stocked())

Очень любит замыкания!

Если значение на входе не содержит Some, то выполнит замыкание в скобках.

|| self.most_stocked():

  • || — между этими полосами передаем параметры в замыкание
  • self.most_stocked() — собственно сама функция замыкания

Замыкания обычно не требуют аннотирования типов входных параметров или возвращаемого значения, как это делается в функциях fn

Если нужно добавить аннотацию, то выглядит так:

#![allow(unused)]
fn main() {
    let expensive_closure = |num: u32| -> u32 {
        println!("calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        num
    };
}

или так

#![allow(unused)]
fn main() {
fn  add_one_v1   (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x|             { x + 1 };
let add_one_v4 = |x|               x + 1  ;
}

это замыкание

#![allow(unused)]
fn main() {
    let clos1 = |x,y| for _ in 1..=y {print!("{x}")};

    clos1("=",20);
}

Замыкание самостоятельно определяет, какой из этих способов владения использовать, исходя из того, что тело функции делает с полученными значениями.

Ссылка на входе (не изменяемое)

#![allow(unused)]
fn main() {
    let list = vec![1, 2, 3];
    println!("Before defining closure: {list:?}");

    let only_borrows = || println!("From closure: {list:?}");

    println!("Before calling closure: {list:?}");
    only_borrows();
    println!("After calling closure: {list:?}");
}

Изменение значения

#![allow(unused)]
fn main() {
let mut list = vec![1, 2, 3];
println!("Before defining closure: {list:?}");

let mut only_borrows = || list.push(7);

only_borrows();
println!("After calling closure: {list:?}");
}

Порождение нового потока (move)

use std::thread;

fn main() {
    let list = vec![1, 2, 3];
    println!("Before defining closure: {list:?}");

    thread::spawn(move || println!("From thread: {list:?}"))
        .join()
        .unwrap();
}

Перемещение захваченных значений из замыканий

Тело замыкания может делать любое из следующих действий: перемещать захваченное значение из замыкания, изменять захваченное значение, не перемещать и не изменять значение или вообще ничего не захватывать из среды.

Трейты замыканий

  1. FnOnce применяется к замыканиям, которые могут быть вызваны один раз.

Замыкание, которое перемещает захваченные значения из своего тела, реализует только FnOnce

#![allow(unused)]
fn main() {
impl<T> Option<T> {
    pub fn unwrap_or_else<F>(self, f: F) -> T
    where
        F: FnOnce() -> T //будет вызвано только один раз
    {
        match self {
            Some(x) => x,
            None => f(),
        }
    }
}

}
  1. FnMut применяется к замыканиям, которые не перемещают захваченные значения из своего тела, но могут изменять захваченные значения.
#![allow(unused)]
fn main() {
    list2.sort_by_key(|r| r.width);
    println!("{list2:#?}");

}
  1. Fn применяется к замыканиям, которые не перемещают захваченные значения из своего тела и не модифицируют захваченные значения, а также к замыканиям, которые ничего не захватывают из своего окружения.