【问题标题】:Map boxed values to mutable dereferenced values in Rust将装箱值映射到 Rust 中的可变取消引用值
【发布时间】:2017-03-01 14:47:23
【问题描述】:

我对一系列盒装值有一个迭代器。我想将此迭代器映射到对装箱值的可变引用。

下面的简化示例展示了如何为不可变引用实现这一点。这个例子编译得很好。

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);

【问题讨论】:

    标签: iterator rust lifetime


    【解决方案1】:

    不可变引用和可变引用之间存在巨大差异。借款的核心原则是:

    异或可变性别名

    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,闭包借用&amp;Vec
    • deref_mut,闭包借用&amp;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 在参数中采用 &amp;mut self
    • 需要IndexMut,它也需要&amp;mut self作为参数,
    • 因此闭包需要对向量进行可变访问。

    因此,每次调用闭包时,它都会访问&amp;mut Vec。因此,每次调用闭包时,NOTHING 都必须为 &amp;mut Vec 加上别名,因此,任何引用都不能泄漏到闭包之外

    这是如何实现的?

    通过收紧你在闭包中访问的引用的生命周期:每次调用闭包时,你都会得到一个&amp;'scope mut Vec 引用,其中'scope 是闭包主体的范围 .

    (这也与重新借用以及&amp;mut T 不是Copy 的事实有关:因为您无法获得内部存储的&amp;mut T 的副本,因为它不是Copy,所以您被交给了通过重新借用&amp;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]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多