Oleg Alexandrov

Closures

Назад

Замыкания - это анонимные функции

// функция:
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;
let add_one_v5 = || 1;

Захват ссылок или передача владения

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);
let mut list = vec![1, 2, 3];
println!("Before defining closure: {:?}", list);

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

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

move

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

se std::thread;

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

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

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

Трейт Описание
FnOnce применяется к замыканиям, которые могут быть вызваны один раз. Все замыкания реализуют по крайней мере этот трейт, потому что все замыкания могут быть вызваны. Замыкание, которое перемещает захваченные значения из своего тела, реализует только FnOnce и ни один из других признаков Fn, потому что оно может быть вызвано только один раз.
FnMut применяется к замыканиям, которые не перемещают захваченные значения из своего тела, но могут изменять захваченные значения. Такие замыкания могут вызываться более одного раза.
Fn применяется к замыканиям, которые не перемещают захваченные значения из своего тела и не модифицируют захваченные значения, а также к замыканиям, которые ничего не захватывают из своего окружения. Такие замыкания могут выполняться более одного раза и не меняют ничего в своём окружении, что важно в таких случаях, как одновременный вызов замыкания несколько раз.

Ограничением трейта, заданным для обобщённого типа F, является FnOnce() -> T, что означает, что F должен вызываться один раз, не принимать никаких аргументов и возвращать T:

// Использование FnOnce в ограничении трейта говорит о том,
// что unwrap_or_else должен вызывать f не более одного раза.
// В теле unwrap_or_else мы видим, что если Option будет равен Some, то f не будет вызван.
// Если же значение Option будет равным None, то f будет вызван один раз.

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(),
        }
    }
}
// Причина, по которой sort_by_key определена как принимающая замыкание FnMut,
// заключается в том, что она вызывает замыкание несколько раз:
// по одному разу для каждого элемента в срезе.

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let mut list = [
        Rectangle { width: 10, height: 1 },
        Rectangle { width: 3, height: 5 },
        Rectangle { width: 7, height: 12 },
    ];

    list.sort_by_key(|r| r.width);
    println!("{:#?}", list);
}




Назад