我有一个针对一系列 Boxed 值的迭代器。我想将此迭代器映射到一个对盒装值的可变引用。
下面的简化示例展示了如何为不可变引用实现这一点。这个例子编译得很好。
let indices = [0usize, 1usize, 2usize];
let vec = vec![Box::new(1.0), Box::new(2.0), Box::new(3.0)];
let i = indices.iter().map(|index| vec[*index].deref()).map(|x| *x + 1.0);
但是,对于可变引用,如下例所示,编译器会产生错误。
let indices = [0usize, 1usize, 2usize];
let mut vec = vec![Box::new(1.0), Box::new(2.0), Box::new(3.0)];
let i = indices.iter().map(|index| vec[*index].deref_mut()).map(|x| *x = *x + 1.0);
编译错误如下:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> src\port_graph/mod.rs:200:40
|
200 | let i = indices.iter().map(|index| vec[*index].deref_mut()).map(|x| *x = *x + 1.0);
| ^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime as defined on the body at 200:39...
--> src\port_graph/mod.rs:200:40
|
200 | let i = indices.iter().map(|index| vec[*index].deref_mut()).map(|x| *x = *x + 1.0);
| ^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `vec`
--> src\port_graph/mod.rs:200:40
|
200 | let i = indices.iter().map(|index| vec[*index].deref_mut()).map(|x| *x = *x + 1.0);
| ^^^
note: but, the lifetime must be valid for the scope of call-site for function at 200:39...
--> src\port_graph/mod.rs:200:40
|
200 | let i = indices.iter().map(|index| vec[*index].deref_mut()).map(|x| *x = *x + 1.0);
| ^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that return value is valid for the call
--> src\port_graph/mod.rs:200:32
|
200 | let i = indices.iter().map(|index| vec[*index].deref_mut()).map(|x| *x = *x + 1.0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
如何解决这个问题?
编辑:对于一个简单的矢量,可以简单地执行以下操作。但是,上面的示例是我想遍历图形(petgraph crate)中节点子集的情况的简化,我不想使用图形本身。
let mut vec = vec![Box::new(1.0), Box::new(2.0), Box::new(3.0)];
let i = vec.iter_mut().map(|boxed| boxed.deref_mut()).map(|x| *x = *x + 1.0);
最佳答案
不可变引用和可变引用之间存在巨大差异。借用的核心原则是:
Aliasing XOR Mutability
Rust 语言保证,如果您有一个可变引用,则没有其他引用为同一对象起别名。
在您的情况下,我们收集...地址,而不是再次映射:
let c: Vec<_> = indices.iter().map(|index| vec[*index].deref())
.map(|x| x as *const _ as usize)
.collect();
println!("{:?}", c);
我们得到向量中元素的地址列表。
这些地址不同的唯一原因是索引不同。如果我们偷偷摸摸,将 indices
初始化为 [0, 1, 2, 1]
,那么我们就会出现别名。
如果我们可以根据运行时属性获得别名,那么我们也不应该获得可变性;因此类型系统强制执行此操作。
它是如何执行的?
闭包借用了vec
。
- 用
deref
,闭包借用了&Vec
- 用
deref_mut
,闭包借用了&mut Vec
你可以亲眼见证第一个:
let i = indices.iter().map(|index| vec[*index].deref())
.map(|x| x as *const _ as usize);
vec[0] = Box::new(3.0);
将失败并提示该向量已被闭包不可变地借用。
第二个是逻辑扩展:
deref_mut
在参数中接受一个&mut self
,- 这需要
IndexMut
也需要一个&mut self
作为参数, - 因此闭包需要对向量进行可变访问。
因此,每次您调用闭包时,它都会访问一个&mut Vec
。因此,每次调用闭包时,NOTHING 必须为此 &mut Vec
设置别名,因此,任何引用都不得泄漏到闭包之外。
这是如何实现的?
通过缩短您在闭包中访问的引用的生命周期:每次调用闭包时,您都会获得一个 &'scope mut Vec
引用,其中 'scope
是闭包主体的作用域,仅此而已。
(这也与重新借用以及 &mut T
不是 Copy
的事实有关:因为无法将内部存储的 的副本交给您>&mut T
因为它不是Copy
,所以你会得到一个重新借用的&mut *vec
,它有一个新的生命周期)。
那有什么解决办法呢?
直接在您有权访问 Vec
的闭包中执行任何和所有计算。在此闭包中,您毕竟拥有可变访问权限。
fn main() {
let indices = [0usize, 1usize, 2usize];
let mut vec = vec![Box::new(1.0), Box::new(2.0), Box::new(3.0)];
let c: Vec<_> =
indices.iter()
.map(|index| {
*vec[*index] = *vec[*index] + 1.0;
*vec[*index]
})
.collect();
println!("{:?}", c);
}
正确显示[2, 3, 4]
。
关于iterator - 将装箱值映射到 Rust 中的可变取消引用值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42534757/