无法在用户定义类型的 `Vec` 中添加分配

tao*_*tao 10 rust

考虑这个代码(Rust Playground):

#[derive(Clone, Copy, Debug)]
struct X(i32);

impl std::ops::AddAssign for X {
    fn add_assign(&mut self, rhs: Self) {
        self.0 += rhs.0;
    }
}

fn main() {
    let mut ary_i32 = [1_i32; 2];
    ary_i32[0] += ary_i32[1]; // OK

    let mut ary_x = [X(1); 2];
    ary_x[0] += ary_x[1]; // OK

    let mut vec_i32 = vec![1_i32; 2];
    vec_i32[0] += vec_i32[1]; // OK

    let mut vec_x = vec![X(1); 2];
    vec_x[0] += vec_x[1]; // error[E0502]: cannot borrow `vec_x` as immutable because it is also borrowed as mutable
}

Run Code Online (Sandbox Code Playgroud)

为什么我只vec_x在线获得 E0502 ?我不明白为什么只允许ary_x和的操作vec_i32。借用检查器是否特别对待内置类型 ( i32, array)?

tao*_*tao 6

我研究了一些资源并阅读了我的代码的 MIR,并设法了解发生了什么。@trentcl
评论将是最好的答案。我尽量写详细。

对于array,IndexIndexMuttrait 没有使用,编译器直接操作数组元素(你可以用 MIR 看到这一点)。所以,这里不存在借贷问题。

Explanating的Vecrustc指导是非常有用的。
首先,两阶段借用不适用于vec_foo[0] += vec_foo[1]语句。
而且,之间的区别i32,并X是造成运营商降低
基本上,语句 likevec_user_defined[0] += vec_user_defined[1]转换为函数调用add_assign(index_mut(...), *index(...)),函数参数从左到右求值。因此,index_mut()借用x性情不定地并index()试图借x,和失败。
但是对于像 那样的内置类型i32,复合赋值运算符不会转换为函数调用,并且 rhs 在 lhs 之前被评估(你可以看到index()index_mut()MIR之前被调用)。所以,对于内置类型,vec_builtin[0] += vec_builtin[1] 作品。

我从lo48576 的文章(日文)中知道这些事情。

我考虑了一些解决方法:

  • 只需使用@sebpuetz所说的中间变量。
  • 转换Vecslice为@trentcl。但这不适用于 multidimensional Vec
  • 编写一些宏来自动引入一个中间变量。我发现rhs_first_assign crate 可以做这样的工作。