问题描述
代码是一种从迭代器生成唯一一组项的低效方法。为了完成这个,我试图使用 Vec
跟踪我看到的值。我相信这个 Vec
需要由最里面的闭包拥有:
fn main(){
let mut seen = vec![];
let items = vec![vec![1i32,2],vec![3],vec![1]];
let a:Vec< _> = items.iter()
.flat_map(move | inner_numbers | {
inner_numbers.iter()。filter_map(move |& number | {
if!seen.contains(& number ){
seen.push(number);
一些(数字)
} else {
无
}
})
}
.collect();
println!({:?},a);但是,编译失败:
$ b 错误:在`FnMut`闭包中无法移出捕获的外部变量[--explain E0507]
- > src / main.rs:7:45
|>
7 |> inner_numbers.iter()。filter_map(move |& number | {
|> ^^^^^^^^^^^^^)不能移出捕获的外部变量在`FnMut`闭包
解决方案这有点令人惊讶, 。
需要一个 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!移出& mut指针
};
inner_numbers.iter()。filter_map(inner)
}
}
impl FnMut(& i32) - >选项< i32> for InnerClosure {...}
这使得非法性更清晰:试图移出& mut OuterClosure
变量。
>,只需捕获可变引用就足够了,因为看到
只在闭包内被修改(未移动)。但是事情太懒了,这不工作...
< anon>:6:41:13:10错误: ``的生命周期太短,不能保证其内容可以安全地重新获得
< anon>:6 inner_numbers.iter()。filter_map(|& number | {
< anon>:7 if you were have(& number){
< anon>:8 seen.push(number);
< anon>:9 Some(number)
< anon> :10} else {
< anon>:11无
...
< anon>:5:21:14:17注意:`seen`该方法调用在5:20 ...
< anon>:5 let a:Vec< _> = items.iter()。flat_map(| inner_numbers | {
< anon>:6 inner_numbers.iter()。filter_map(|& number | {
< anon>:7 if!seen.contains(& number){
< anon>:8 seen.push );
< anon>:9一些(数字)
< anon>:10} else {
...
< anon>:5:59:14 :6 note:...但是`seen'只对在块5:58定义的生命周期有效
< anon>:5 let a:Vec< _> = items.iter()。flat_map(| inner_numbers | {
< anon>:6 inner_numbers.iter()。filter_map(|& number | {
< anon>:7 if! .contains(& number){
< anon>:8 seen.push(number);
< anon>:9 Some(number)
< anon>:10} else {
...
删除 move
s使得闭包捕获的工作像
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& InnerClosure {
seen:& mut * self.seen //无法移出,所以必须是reborrow
};
inner_numbers.iter()。filter_map(inner)
}
}
impl<'a> FnMut(& i32) - >选项< i32>对于内部环境{...}
(我命名为
生命在这一个,为教育目的。)
这种情况肯定更微妙。 FilterMap
迭代器在内部存储闭包,意味着闭包值中的任何引用(即它捕获的任何引用)必须有效,只要 FilterMap
的值被抛出,并且对于& mut
引用,任何引用都必须小心不要别名。 p>
编译器不能确定 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
引用和'a
是 struct
。移动内部& mut
是非法的:无法将类似& mut
的仿射类型移出参考(它将与& Vec< i32>
),所以唯一的选择是reborrow。 reborrow是通过外部引用,所以不能超过它,也就是说,& mut * self.seen
reborrow是一个& b mut Vec< i32>
,而不是&'a mut Vec
。
$ b b 这使得内部闭包有类型 InnerClosure
,因此 call_mut
以返回 FilterMap 。很抱歉, call_mut
仅定义
pub trait FnMut< Args> {
type Output;
externrust-callfn call_mut(& mut self,args:Args) - > Self :: Output;
}
特别的, self
引用本身和返回的值,因此尝试返回具有该链接的 InnerClosure<'b>
是非法的。这是为什么编译器抱怨生命周期太短,无法重新开发。
这非常类似于 Iterator :: next
方法,这里的代码失败的原因基本上是相同的原因,一个不能有迭代器本身拥有的内存引用迭代器。 (我想象一个(迭代器与& mut self 和下一个
)中的返回值将能够提供一个 flat_map
代码接近写:需要具有类似链接的关闭特性。)
解决方法包括:
- 由Renato Zannon建议的
RefCell
允许将看作
&
。除了将& mut Vec< i32>
更改为& Vec< i32> $ c $,desugared闭包代码基本相同c>。这种改变意味着&'b mut&'a RefCell< Vec< i32>< / code>的reborrow可以是&'a ...
。& mut
这是一个字面的副本,所以生命周期得以保留。
- 避免迭代器的惰性,避免返回内部闭包,特别是
.collect ::< Vec&
) 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){
visible.push(number);
一些(数字)
} else {
无
}
})collect ::< Vec< _> ().into_iter()
})。
println!({:?},a);
}
我想象 RefCell
版本更高效。
The code is an inefficient way of producing a unique set of items from an iterator. To accomplish this, I am attempting to use a Vec
to keep track of values I've seen. I believe that this Vec
needs to be owned by the innermost closure:
fn main() {
let mut seen = vec![];
let items = vec![vec![1i32, 2], vec![3], vec![1]];
let a: Vec<_> = items.iter()
.flat_map(move |inner_numbers| {
inner_numbers.iter().filter_map(move |&number| {
if !seen.contains(&number) {
seen.push(number);
Some(number)
} else {
None
}
})
})
.collect();
println!("{:?}", a);
}
However, compilation fails with:
error: cannot move out of captured outer variable in an `FnMut` closure [--explain E0507]
--> src/main.rs:7:45
|>
7 |> inner_numbers.iter().filter_map(move |&number| {
|> ^^^^^^^^^^^^^^ cannot move out of captured outer variable in an `FnMut` closure
解决方案 This is a little surprising, but isn't a bug.
flat_map
takes a FnMut
, since it needs to call the closure multiple times, and so the code with move
on the inner closure fails because that closure is created multiple times, once for each inner_numbers
. If I write the closures in explicit form (i.e. a struct that stores the captures and an implementation of one of the closure traits) your code looks (a bit) like
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 { ... }
Which makes the illegality clearer: attempting to move out of the &mut OuterClosure
variable.
Theoretically, just capturing a mutable reference is sufficient, since the seen
is only being modified (not moved) inside the closure. However things are too lazy for this to work...
<anon>:6:41: 13:10 error: lifetime of `seen` is too short to guarantee its contents can be safely reborrowed
<anon>:6 inner_numbers.iter().filter_map(|&number| {
<anon>:7 if !seen.contains(&number) {
<anon>:8 seen.push(number);
<anon>:9 Some(number)
<anon>:10 } else {
<anon>:11 None
...
<anon>:5:21: 14:17 note: `seen` would have to be valid for the method call at 5:20...
<anon>:5 let a: Vec<_> = items.iter().flat_map(|inner_numbers| {
<anon>:6 inner_numbers.iter().filter_map(|&number| {
<anon>:7 if !seen.contains(&number) {
<anon>:8 seen.push(number);
<anon>:9 Some(number)
<anon>:10 } else {
...
<anon>:5:59: 14:6 note: ...but `seen` is only valid for the lifetime as defined on the block at 5:58
<anon>:5 let a: Vec<_> = items.iter().flat_map(|inner_numbers| {
<anon>:6 inner_numbers.iter().filter_map(|&number| {
<anon>:7 if !seen.contains(&number) {
<anon>:8 seen.push(number);
<anon>:9 Some(number)
<anon>:10 } else {
...
Removing the move
s makes the closure captures work like
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> { ... }
(I've named the &mut self
lifetime in this one, for pedagogical purposes.)
This case is definitely more subtle. The FilterMap
iterator stores the closure internally, meaning any references in the closure value (that is, any references it captures) have to be valid as long as the FilterMap
values are being thrown around, and, for &mut
references, any references have to be careful to be non-aliased.
The compiler can't be sure flat_map
won't, e.g. store all the returned iterators in a Vec<FilterMap<...>>
which would result in a pile of aliased &mut
s... very bad! I think this specific use of flat_map
happens to be safe, but I'm not sure it is in general, and there's certainly functions with the same style of signature as flat_map
(e.g. map
) would definitely be unsafe
. (In fact, replacing flat_map
with map
in the code gives the Vec
situation I just described.)
For the error message: self
is effectively (ignoring the struct wrapper) &'b mut (&'a mut Vec<i32>)
where 'b
is the lifetime of &mut self
reference and 'a
is the lifetime of the reference in the struct
. Moving the inner &mut
out is illegal: can't move an affine type like &mut
out of a reference (it would work with &Vec<i32>
, though), so the only choice is to reborrow. A reborrow is going through the outer reference and so cannot outlive it, that is, the &mut *self.seen
reborrow is a &'b mut Vec<i32>
, not a &'a mut Vec<i32>
.
This makes the inner closure have type InnerClosure<'b>
, and hence the call_mut
method is trying to return a FilterMap<..., InnerClosure<'b>>
. Unfortunately, the FnMut
trait defines call_mut
as just
pub trait FnMut<Args> {
type Output;
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
In particular, there's no connection between the lifetime of the self
reference itself and the returned value, and so it is illegal to try to return InnerClosure<'b>
which has that link. This is why the compiler is complaining that the lifetime is too short to be able to reborrow.
This is extremely similar to the Iterator::next
method, and the code here is failing for basically the same reason that one cannot have an iterator over references into memory that the iterator itself owns. (I imagine a "streaming iterator" (iterators with a link between &mut self
and the return value in next
) library would be able to provide a flat_map
that works with the code nearly written: would need "closure" traits with a similar link.)
Work-arounds include:
- the
RefCell
suggested by Renato Zannon, which allows seen
to be borrowed as a shared &
. The desugared closure code is basically the same other than changing the &mut Vec<i32>
to &Vec<i32>
. This change means "reborrow" of the &'b mut &'a RefCell<Vec<i32>>
can just be a copy of the &'a ...
out of the &mut
. It's a literal copy, so the lifetime is retained. - avoiding the laziness of iterators, to avoid returning the inner closure, specifically
.collect::<Vec<_>>()
ing inside the loop to run through the whole filter_map
before returning.
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);
}
I imagine the RefCell
version is more efficient.
这篇关于如何将捕获的变量移动到闭包中的闭包中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!