为什么允许在一个表达式中同时包含数字类型的向量的不可变和可变借项?

时间:2020-05-11 14:54:24

标签: rust

a是一种Vec<i32>,可以在一个表达式中以可变和不可变的方式引用:

fn main() {
    let mut a = vec![0, 1];
    a[0] += a[1]; // OK
}

我认为编译此文件是因为i32实现了Copy,所以我创建了另一个实现Copy的类型,并像第一个示例一样对其进行了编译,但是失败了:

use std::ops::AddAssign;

#[derive(Clone, Copy, PartialEq, Debug, Default)]
struct MyNum(i32);

impl AddAssign for MyNum {
    fn add_assign(&mut self, rhs: MyNum) {
        *self = MyNum(self.0 + rhs.0)
    }
}

fn main() {
    let mut b = vec![MyNum(0), MyNum(1)];
    b[0] += b[1];
}

playground

error[E0502]: cannot borrow `b` as immutable because it is also borrowed as mutable
  --> src/main.rs:14:13
   |
14 |     b[0] += b[1];
   |     --------^---
   |     |       |
   |     |       immutable borrow occurs here
   |     mutable borrow occurs here
   |     mutable borrow later used here
  1. 即使我的MyNum实现了i32,为什么我的Copy的行为也与double[,] emptyArray = new double[usedImage.Width, usedImage.Height]; NetVips.Image emptyImage = NetVips.Image.NewFromMemory(emptyArray, usedImage.Width, usedImage.Height, 1, "uchar"); sources.Add(emptyImage); 不同?
  2. 为什么在一个表达式中可变和不可变地引用向量?

1 个答案:

答案 0 :(得分:5)

我相信您在这里看到的是primitive types实际上没有称呼它们的std::ops等效项。那些std::ops可能只是为了无缝特征扩展而包含在内,等等。我认为博客文章Rust Tidbits: What Is a Lang Item?部分地解释了这一点。

我导出了您的示例that works with primitive types的MIR。我得到了:

    bb5: {
        StorageDead(_9);                 // bb5[0]: scope 1 at src/main.rs:6:8: 6:9
        _10 = CheckedAdd((*_8), move _5); // bb5[1]: scope 1 at src/main.rs:6:5: 6:17
        assert(!move (_10.1: bool), "attempt to add with overflow") -> [success: bb6, unwind: bb4]; // bb5[2]: scope 1 at src/main.rs:6:5: 6:17
    }

导出错误代码的MIR时遇到很多困难。在没有借阅检查的情况下输出MIR对我来说是新的,我不知道该怎么做。

This游乐场有一个非常相似的东西,但是会编译:)

它给了我一个对add_assign的实际呼叫:

    bb3: {
        _8 = _9;                         // bb3[0]: scope 1 at src/main.rs:14:5: 14:9
        StorageDead(_10);                // bb3[1]: scope 1 at src/main.rs:14:8: 14:9
        StorageLive(_11);                // bb3[2]: scope 1 at src/main.rs:14:14: 14:22
        (_11.0: i32) = const 1i32;       // bb3[3]: scope 1 at src/main.rs:14:14: 14:22
                                         // ty::Const
                                         // + ty: i32
                                         // + val: Value(Scalar(0x00000001))
                                         // mir::Constant
                                         // + span: src/main.rs:14:20: 14:21
                                         // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) }
        _7 = const <MyNum as std::ops::AddAssign>::add_assign(move _8, move _11) -> [return: bb5, unwind: bb4]; // bb3[4]: scope 1 at src/main.rs:14:5: 14:22
                                         // ty::Const
                                         // + ty: for<'r> fn(&'r mut MyNum, MyNum) {<MyNum as std::ops::AddAssign>::add_assign}
                                         // + val: Value(Scalar(<ZST>))
                                         // mir::Constant
                                         // + span: src/main.rs:14:5: 14:22
                                         // + literal: Const { ty: for<'r> fn(&'r mut MyNum, MyNum) {<MyNum as std::ops::AddAssign>::add_assign}, val: Value(Scalar(<ZST>)) }
    }

原始案例如何通过借阅检查器?由于未调用add_assign,因此可以在需要可变引用之前删除不可变引用。 MIR只是较早地取消了对所需位置的引用,并按值将其传递出去。

    bb3: {
        _5 = (*_6);                      // bb3[0]: scope 1 at src/main.rs:6:13: 6:17
        StorageDead(_7);                 // bb3[1]: scope 1 at src/main.rs:6:16: 6:17
        ...
    }