Как замыкание, использующее ключевое слово `move`, может создать замыкание FnMut? - PullRequest
0 голосов
/ 02 мая 2018

До этого момента я думал, что move |...| {...} будет перемещать переменные внутри замыкания, а замыкание будет реализовывать только FnOnce, потому что переменные можно перемещать только один раз. Однако, к моему удивлению, я обнаружил, что этот код работает:

extern crate futures;

use futures::stream;
use futures::stream::{Stream, StreamExt};
use std::rc::Rc;

#[derive(Debug)]
struct Foo(i32);

fn bar(r: Rc<Foo>) -> Box<Stream<Item = (), Error = ()> + 'static> {
    Box::new(stream::repeat::<_, ()>(()).map(move |_| {
        println!("{:?}", r);
    }))
}

fn main() {
    let r = Rc::new(Foo(0));
    let _ = bar(r);
}

Несмотря на то, что map имеет эту подпись:

fn map<U, F>(self, f: F) -> Map<Self, F>
where
    F: FnMut(Self::Item) -> U, 

Меня удивляет, что закрытие FnMut было создано при использовании ключевого слова move, и у него даже есть время жизни 'static. Где я могу найти некоторые подробности о move? Или как это на самом деле работает?

Ответы [ 2 ]

0 голосов
/ 03 мая 2018

Да, этот момент довольно запутанный, и я думаю, что формулировка книги Rust вносит свой вклад. После прочтения я подумал так же, как и вы: что move замыкание обязательно FnOnce и что не move замыкание FnMut (а также может быть Fn). Но это как бы назад от реальной ситуации.

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

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

Как вы упомянули, вы можете потреблять значение внутри замыкания, вызывая drop для него или другими способами, но наиболее распространенный случай - вернуть его из замыкания, что выводит его из замыкания. , Вот самый простой пример:

let s = String::from("hello world");
let my_fnonce = move || { s };

Если тело затвора не потребляет ни одного из его захватов, то это FnMut, независимо от того, было ли оно move или нет. Если он также не изменяет ни один из своих снимков, он также Fn; любое закрытие, равное Fn, также FnMut. Вот простой пример, хотя и не очень хороший.

let s = "hello world";
let my_fn = move || { s.length() }

Краткое описание

Модификатор move контролирует, как захваты перемещаются в замыкание, когда оно создано . FnMut членство определяется тем, как перехваты перемещаются из закрытия (или используются каким-либо другим способом), когда выполнено .

0 голосов
/ 02 мая 2018

До этого момента я думал, что move |...| {...} будет перемещать переменные внутри замыкания, а замыкание будет реализовывать только FnOnce, потому что вы можете перемещать переменные только один раз.

Переменные перемещаются при создании замыкания, а не при его вызове. Поскольку вы создаете только одно замыкание, перемещение происходит только один раз - независимо от того, как часто map вызывает функцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...