Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Захоплення

Замикання є невід’ємно гнучкими і робитимуть те, що вимагає функціональність, щоб змусити замикання працювати без анотації. Це дозволяє захопленню гнучко пристосовуватися до випадку використання, інколи переміщуючи, а інколи запозичуючи. Замикання можуть захоплювати змінні:

  • за посиланням: &T
  • за змінним посиланням: &mut T
  • за значенням: T

Вони переважно захоплюють змінні за посиланням і спускаються нижче лише за потреби.

fn main() {
    use std::mem;

    let color = String::from("green");

    // Замикання для друку `color`, яке негайно запозичує (`&`) `color` і
    // зберігає запозичення та замикання у змінній `print`. Воно залишатиметься
    // запозиченим, доки `print` не буде використано востаннє.
    //
    // `println!` потребує лише аргументів за незмінним посиланням, тож воно не
    // накладає нічого більш обмежувального.
    let print = || println!("`color`: {}", color);

    // Виклик замикання з використанням запозичення.
    print();

    // `color` знову можна запозичити незмінно, бо замикання тримає лише
    // незмінне посилання на `color`.
    let _reborrow = &color;
    print();

    // Переміщення або повторне запозичення дозволено після фінального використання `print`
    let _color_moved = color;


    let mut count = 0;
    // Замикання для збільшення `count` могло б взяти або `&mut count`, або `count`
    // але `&mut count` менш обмежувальне, тож воно його й бере. Негайно
    // запозичує `count`.
    //
    // На `inc` потрібен `mut`, бо всередині зберігається `&mut`. Отже,
    // виклик замикання змінює `count`, що потребує `mut`.
    let mut inc = || {
        count += 1;
        println!("`count`: {}", count);
    };

    // Виклик замикання з використанням змінного запозичення.
    inc();

    // Замикання все ще змінно запозичує `count`, бо його буде викликано пізніше.
    // Спроба повторного запозичення призведе до помилки.
    // let _reborrow = &count;
    // ^ TODO: try uncommenting this line.
    inc();

    // Тепер замиканню більше не потрібно запозичувати `&mut count`. Тому
    // можливо повторно запозичити без помилки
    let _count_reborrowed = &mut count;


    // Тип без копіювання.
    let movable = Box::new(3);

    // `mem::drop` потребує `T`, тож це має брати за значенням. Тип із копіюванням
    // скопіювався б у замикання, залишивши оригінал недоторканим.
    // Тип без копіювання має переміститися, і тому `movable` негайно переміщується в
    // замикання.
    let consume = || {
        println!("`movable`: {:?}", movable);
        mem::drop(movable);
    };

    // `consume` споживає змінну, тож це можна викликати лише один раз.
    consume();
    // consume();
    // ^ TODO: Try uncommenting this line.
}

Використання move перед вертикальними рисками примушує замикання брати у володіння захоплені змінні:

fn main() {
    // `Vec` має семантику без копіювання.
    let haystack = vec![1, 2, 3];

    let contains = move |needle| haystack.contains(needle);

    println!("{}", contains(&1));
    println!("{}", contains(&4));

    // println!("There're {} elements in vec", haystack.len());
    // ^ Uncommenting above line will result in compile-time error
    // because borrow checker doesn't allow re-using variable after it
    // has been moved.

    // Removing `move` from closure's signature will cause closure
    // to borrow _haystack_ variable immutably, hence _haystack_ is still
    // available and uncommenting above line will not cause an error.
}

Дивіться також:

Box та std::mem::drop