Вложенные замыкания сложны.
Учтите это:
fn use_a_fn_multiple_times(f: impl Fn(String)) {
f("foo".to_owned());
f("bar".to_owned());
}
fn use_fn_once(f: impl FnOnce() -> Vec<u8>) {
println!("Bytes: {:?}", f());
}
fn main() {
use_a_fn_multiple_times(|a: String| {
use_fn_once(move || a.into_bytes());
});
}
Игровая площадка
Обратите внимание, что внутреннее закрытие захватывает a
при движении. Это отлично. Внешнему закрытию принадлежит a
, и он может делать с ним все, что хочет, в том числе перемещать его во внутреннее закрытие (которое, поскольку оно потребляет захваченное значение, представляет собой FnOnce
).
Внешнее замыкание вызывается несколько раз, каждый раз с новой строкой, и каждый раз, когда создается новое внутреннее замыкание, захватывающее эту строку.
Но что, если то, что вы хотите запечатлеть, приходит еще дальше?
fn use_a_fn_multiple_times(f: impl Fn(String)) {
f("foo".to_owned());
f("bar".to_owned());
}
fn use_fn_once(f: impl FnOnce() -> Vec<u8>) {
println!("Bytes: {:?}", f());
}
fn main() {
let outer_s = "see:".to_owned();
use_a_fn_multiple_times(|a: String| {
use_fn_once(move || {
let mut v = outer_s.into_bytes();
v.extend(a.into_bytes());
v
});
});
}
Детская площадка
Затем вы получите сообщение об ошибке (за исключением Fn
против FnMut
, что не имеет значения для проблемы). Внутреннее закрытие создается заново при каждом обращении к внешнему закрытию (так должно быть, потому что оно должно захватывать a
каждый раз), но оно пытается захватить outer_s
путем перемещения каждый раз. Это не может работать; после первого раза outer_s
перемещается и, таким образом, становится недействительным.
Чтобы сопоставить это с вашим кодом, было бы неправильно говорить "Closure B захватывает notificator
", потому что не существует только одного Closure B. Там столько столько, сколько необходимо, однако часто ваши вложенные and_then
и for_each
звонки будут в конечном итоге в этом куске кода. Но только один может захватить с ходу.
Итак, чтобы решить эту проблему, вам нужно либо убедиться, что есть только одно Замыкание B, либо убедиться, что у вас достаточно mpsc::Sender
s для каждого.
Первый способ заключается в извлечении замыкания из вложенного контекста.
let closure_b = move |b| {
notificator.send(b.len());
Ok(())
};
proxy.something()
.and_then(move |sub| {
sub.for_each(move |a| { // <---- Closure A
proxy.something_else(a)
.and_then(closure_b)
.or_else(|e| {
panic!("oops {}", e);
Ok(())
})
})
})
.map_err(|e| {
()
})
за исключением того, что это не сработает, поскольку теперь Closure A сталкивается с той же проблемой, поэтому вам придется делать это несколько раз:
let closure_b = move |b| {
notificator.send(b.len());
Ok(())
};
let closure_a = move |a| {
proxy.something_else(a)
.and_then(closure_b)
.or_else(|e| {
panic!("oops {}", e);
Ok(())
})
};
proxy.something()
.and_then(move |sub| {
sub.for_each(closure_a)
})
.map_err(|e| {
()
})
Второй способ включает множество clone()
вызовов, и, поскольку я не могу проверить ваш код, я не буду пытаться его написать.
Однако, когда все сказано и сделано, ваш код все равно потерпит неудачу, потому что вы выходите из Proxy
, одновременно пытаясь его использовать.