为什么我不能在左侧使用带有可变引用的运算符?

Edw*_*ers 6 rust

use std::ops::Add;

#[derive(Debug)]
struct Sample {
    pub value: usize,
}
impl std::ops::Add<&Sample> for &Sample {
    type Output = Sample;

    fn add(self, other: &Sample) -> Sample {
        Sample {
            value: self.value + other.value,
        }
    }
}

fn main() {
    let mut a = Sample { value: 0 };
    let b = Sample { value: 1 };

    let mut_a = &mut a;
    let immut_b = &b;

    println!("Works: {:?}", mut_a.add(immut_b));
    println!("And this works: {:?}", immut_b.add(mut_a));
    println!("Also works:: {:?}", immut_b + mut_a);
    println!("Still works:: {:?}", &*mut_a + immut_b);
    println!("Fails: {:?}", mut_a + immut_b);
}

Run Code Online (Sandbox Code Playgroud)

在此代码中add需要两个&Samples。在 main 方法中,我创建了对两个不同Samples 的两个引用,一个是可变的,一个是不可变的。我可以按add任一顺序显式调用它们,但如果我使用+运算符,它会抱怨可变引用首先出现。(将其重新借用为不可变也可以使其工作。)我希望可变引用在不可变引用所在的任何地方都有效。是什么赋予了?

为了加深神秘感,我尝试对基本的 s 做同样的事情i32-

    let mut x: i32 = 0;
    let y: i32 = 1;
    let mut_x = &mut x;
    let immut_y = &y;
    println!("But this doesn't work? {:?}", immut_y + mut_x);
    println!("Or this? {:?}", mut_x + immut_y);
    println!("And this does? {:?}", mut_x.add(immut_y));
    println!("But not this? {:?}", immut_y.add(mut_x));S
Run Code Online (Sandbox Code Playgroud)

并得到了完全不同的意外行为 - 现在我无法使用+运算符以任何顺序添加它们,并且只有在可变对象上调用该函数时,显式调用才有效。

Iwa*_*Iwa 2

对于第一个片段,情况是,虽然方法调用是通过自动解引用解决的,但重载运算符却不是。


println!("Works: {:?}", mut_a.add(immut_b));
Run Code Online (Sandbox Code Playgroud)

通过自动解引用,我们能够将&mut Sample参数传递到&Sample预期的位置。为了决定调用哪个方法,rust 大致做了:

  • 创建方法调用的候选列表:[&mut Sample, &&mut Sample, &mut &mut Sample, Sample, &Sample, &mut Sample]
  • 对于列表中的每个候选类型,它都会查找一个名为的方法add,其接收的类型&Sampleself类型完全相同。找到该点后&Sample,rust 会自动取消引用/引用mut_a以使调用正常工作。所以脱糖后的代码如下所示:
<&Sample>::add(&*mut_a, immut_b);
Run Code Online (Sandbox Code Playgroud)

仅此一点并不能解释有关自动取消引用的更多信息,您应该检查上面的链接。


println!("And this works: {:?}", immut_b.add(mut_a));
Run Code Online (Sandbox Code Playgroud)

自动取消引用会在第一步解析方法,而无需任何自动取消引用/引用。


println!("Also works:: {:?}", immut_b + mut_a);
Run Code Online (Sandbox Code Playgroud)

由于这不是方法调用表达式,因此根本不存在自动取消引用。Add推断特征的实现类型。相当于:

::std::ops::Add::add(immut_b, mut_a);
Run Code Online (Sandbox Code Playgroud)
println!("Still works:: {:?}", &*mut_a + immut_b);
Run Code Online (Sandbox Code Playgroud)

与上面相同,只是没有对右操作数进行强制。


println!("Fails: {:?}", mut_a + immut_b);
Run Code Online (Sandbox Code Playgroud)

Rust 无法推断特征的实现类型,因为它只查找不存在的Addfor的实现。&mut Sample与方法调用表达式不同,在这种情况下没有自动取消引用,并且 rust 不会做任何进一步的事情来解决。粗略脱糖的代码(也因同样的原因失败):

<&mut Sample as ::std::ops::Add<&Sample>>::add(mut_a, immut_b);
Run Code Online (Sandbox Code Playgroud)