Я подумал, что мне может понадобиться move
закрытие, чтобы оно получило право владения a2b
Это верно, вам нужно move
на внешнем закрытие. Без move
закрытие будет захватывать a2b
по ссылке. Однако a2b
является локальным параметром, и возвращение закрытия, имеющего ссылку на локальное значение, недопустимо.
Добавление move
к внутреннему замыканию приводит к ошибке, поскольку функция возвращает Fn
закрытие. Для этого аргумента давайте рассмотрим функцию map_option_5
:
pub fn map_option_5<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(move |a| a2b(a))
})
}
Если внутреннее замыкание захватывает a2b
по значению, а внешнее замыкание также является замыканием move
, тогда оба замыкания заканчивают захватом a2b
по значению. По правилам владения, только одно из затворов может одновременно иметь a2b
, поэтому, когда вызывается внешнее затвор, оно выдвигается a2b
из себя (разрушая внешнее затвор) и во внутреннее затвор (которое возможно только для FnOnce
замыканий, поскольку они принимают self
, а не &mut self
или &self
). Причиной сообщения об ошибке является то, что мы возвращаем закрытие Fn
, а не закрытие FnOnce
. Мы действительно могли бы исправить это, возвращая FnOnce
замыкание (но тогда оно не могло быть вызвано более одного раза):
pub fn map_option_5a<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<FnOnce(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(move |a| a2b(a))
})
}
Теперь давайте обсудим, почему map_option
работает, пока map_option_2
нет. Проблема связана с тем, что Option::map
становится владельцем аргумента замыкания. Таким образом, мы оказываемся в ситуации, аналогичной map_option_5
выше. Option::map
занимает FnOnce
, потому что это нужно только вызвать его не более одного раза. Изменение a2b
на Box<FnOnce(A) -> B>
не поможет, потому что его можно использовать во многих вызовах map
.
Существует способ избежать внутреннего закрытия: передайте ссылку на a2b
до map
. Это работает, потому что
Box<F> where F: Fn<A>
реализует Fn<A>
и &F where F: Fn<A>
реализует FnOnce<A>
(а также Fn<A>
хотя здесь это не имеет значения).
pub fn map_option_2a<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(&a2b)
})
}
Закрытие по-прежнему становится владельцем a2b
, но оно не потребляет при вызове, поэтому замыкание может вызываться несколько раз.
map_option
работает, потому что его внешнее замыкание не должно потреблять a2b
. Внутренняя крышка фиксирует a2b
по ссылке от внешней крышки. Это работает, потому что для вызова Fn
замыкания требуется только общая ссылка на замыкание.