iterator - 将装箱值映射到 Rust 中的可变取消引用值

标签 iterator rust lifetime

我有一个针对一系列 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/

相关文章:

java - FindBugs 警告 : Inefficient use of keySet iterator

java - ArrayList 作为 HashMap 的值仅返回 ArrayList 的最后一个元素

generics - 在通用变量中存储嵌套特征实例

macros - 是否可以防止 Rust 中的宏重复相同的参数?

rust - println 中链迭代器之间的不同行为!从变量

c++ - 返回引用也会延长它的生命周期吗?

c++ - 在 C++ 中访问列表列表的元素

java - 如何在 Java 中以线程安全的方式进行迭代循环

c# - 不同 ASP.NET MVC 组件中的对象作用域和生命周期是什么?

struct - 为什么要在结构中对引用使用相同的生命周期?