Ответ 1
Это немного удивительно, но это не ошибка.
flat_map
принимает FnMut
, так как нужно несколько раз вызвать его закрытие. Код с move
на внутреннем замыкании не работает, потому что это замыкание создается несколько раз, один раз для каждого inner_numbers
. Если я пишу закрытие в явном виде (т.е. Структура, в которой хранятся захваты и реализация одной из характеристик замыкания), ваш код выглядит (немного), например
struct OuterClosure {
seen: Vec<i32>
}
struct InnerClosure {
seen: Vec<i32>
}
impl FnMut(&Vec<i32>) -> iter::FilterMap<..., InnerClosure> for OuterClosure {
fn call_mut(&mut self, (inner_numbers,): &Vec<i32>) -> iter::FilterMap<..., InnerClosure> {
let inner = InnerClosure {
seen: self.seen // uh oh! a move out of a &mut pointer
};
inner_numbers.iter().filter_map(inner)
}
}
impl FnMut(&i32) -> Option<i32> for InnerClosure { ... }
Из-за чего становится яснее противозаконность: попытка выйти из переменной &mut OuterClosure
.
Теоретически достаточно захватить изменяемую ссылку, поскольку seen
только изменяется (не перемещается) внутри замыкания. Однако для этого нужно слишком лениться...
error: lifetime of `seen` is too short to guarantee its contents can be safely reborrowed
--> src/main.rs:9:45
|
9 | inner_numbers.iter().filter_map(|&number| {
| ^^^^^^^^^
|
note: `seen` would have to be valid for the method call at 7:20...
--> src/main.rs:7:21
|
7 | let a: Vec<_> = items.iter()
| _____________________^
8 | | .flat_map(|inner_numbers| {
9 | | inner_numbers.iter().filter_map(|&number| {
10| | if !seen.contains(&number) {
... |
17| | })
18| | .collect();
| |__________________^
note: ...but `seen` is only valid for the lifetime as defined on the body at 8:34
--> src/main.rs:8:35
|
8 | .flat_map(|inner_numbers| {
| ___________________________________^
9 | | inner_numbers.iter().filter_map(|&number| {
10| | if !seen.contains(&number) {
11| | seen.push(number);
... |
16| | })
17| | })
| |_________^
Удаление move
приводит к тому, что фиксации закрытия работают как
struct OuterClosure<'a> {
seen: &'a mut Vec<i32>
}
struct InnerClosure<'a> {
seen: &'a mut Vec<i32>
}
impl<'a> FnMut(&Vec<i32>) -> iter::FilterMap<..., InnerClosure<??>> for OuterClosure<'a> {
fn call_mut<'b>(&'b mut self, inner_numbers: &Vec<i32>) -> iter::FilterMap<..., InnerClosure<??>> {
let inner = InnerClosure {
seen: &mut *self.seen // can't move out, so must be a reborrow
};
inner_numbers.iter().filter_map(inner)
}
}
impl<'a> FnMut(&i32) -> Option<i32> for InnerClosure<'a> { ... }
(Я назвал время жизни &mut self
в этом, для педагогических целей.)
Этот случай определенно более тонкий. Итератор FilterMap
хранит внутреннее закрытие, то есть любые ссылки в значении замыкания (то есть любые ссылки, которые он фиксирует) должны быть действительными до тех пор, пока значения FilterMap
будут выбрасываться, а для &mut
ссылок, любые ссылки должны быть осторожны, чтобы не сглаживаться.
Компилятор не может быть уверен, что flat_map
не будет, например. сохраните все возвращенные итераторы в Vec<FilterMap<...>>
, что приведет к тому, что куча псевдонимов &mut
s... очень плохая! Я думаю, что это конкретное использование flat_map
оказывается безопасным, но я не уверен, что это вообще, и, безусловно, функции с тем же типом подписи, что и flat_map
(например, map
), определенно будут unsafe
. (Фактически, замена flat_map
на map
в коде дает ситуацию Vec
, которую я только что описал.)
Для сообщения об ошибке: self
эффективно (игнорирование обертки структуры) &'b mut (&'a mut Vec<i32>)
где 'b
- это время жизни &mut self
reference, а 'a
- время жизни ссылки в struct
. Перемещение внутреннего &mut
вне незаконно: не может перенести аффинный тип типа &mut
из ссылки (это будет работать с &Vec<i32>
, хотя), поэтому единственный выбор - перебор. Перебор происходит через внешнюю ссылку и поэтому не может ее пережить, т.е. &mut *self.seen
reborrow - это &'b mut Vec<i32>
, а не &'a mut Vec<i32>
.
Это делает внутреннее замыкание типа InnerClosure<'b>
, и, следовательно, метод call_mut
пытается вернуть a FilterMap<..., InnerClosure<'b>>
. К сожалению, показатель FnMut
определяет call_mut
как просто
pub trait FnMut<Args>: FnOnce<Args> {
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
В частности, нет связи между временем жизни самой ссылки self
и возвращаемым значением, и поэтому незаконно пытаться вернуть InnerClosure<'b>
, у которого есть эта ссылка. Вот почему компилятор жалуется, что срок службы слишком короткий, чтобы иметь возможность переубеждать.
Это очень похоже на метод Iterator::next
, и здесь код не работает, по той же причине, что итератор не может ссылаться на ссылки в памяти, которыми владеет сам итератор. (Я предполагаю, что "потоковой итератор" (итераторы со связью между &mut self
и возвращаемым значением в next
) библиотеке для обеспечения flat_map
, который работает с почти написанным кодом: для этого нужны черты "закрытия" с аналогичной ссылкой.)
Работы включают:
-
RefCell
, предложенный Ренато Занноном, который позволяетseen
заимствовать как общий&
. Код закрытого закрытия в основном такой же, как изменение&mut Vec<i32>
до&Vec<i32>
. Это изменение означает, что "reborrow" из&'b mut &'a RefCell<Vec<i32>>
может быть просто копией&'a ...
из&mut
. Это буквальная копия, поэтому продолжительность жизни сохраняется. - избегая лени итераторов, чтобы избежать возврата внутреннего закрытия, в частности,
.collect::<Vec<_>>()
внутри цикла, чтобы пройти через весьfilter_map
перед возвратом.
fn main() {
let mut seen = vec![];
let items = vec![vec![1i32, 2], vec![3], vec![1]];
let a: Vec<_> = items
.iter()
.flat_map(|inner_numbers| {
inner_numbers
.iter()
.filter_map(|&number| if !seen.contains(&number) {
seen.push(number);
Some(number)
} else {
None
})
.collect::<Vec<_>>()
.into_iter()
})
.collect();
println!("{:?}", a);
}
Я полагаю, что версия RefCell
более эффективна.