You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何左侧为可变引用时无法使用+运算符?

关于Rust中Add trait与引用兼容性的疑问

首先看自定义Sample结构体的代码:

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

在这段代码中,我们为&Sample实现了Add<&Sample> trait,add方法接收两个&Sample类型参数。在main函数中创建了可变引用mut_a和不可变引用immut_b后,出现了这些现象:

  • 显式调用add方法时,无论可变引用在左还是右都能正常运行
  • 使用+运算符时,不可变引用在左(immut_b + mut_a)可以运行,但可变引用在左(mut_a + immut_b)会报错,只有显式把可变引用重新借用为不可变引用(&*mut_a + immut_b)才能正常运行

这里的疑问是:可变引用不是应该可以替代任何场景下的不可变引用吗?为什么会出现这种差异?

再看基础类型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));

此时的行为完全不同:

  • 无论以什么顺序使用+运算符都会报错
  • 只有以可变引用为调用者时,显式调用add方法才能正常运行,反过来用不可变引用调用add传入可变引用会报错

核心原因:Deref强制转换与运算符重载的类型匹配规则

自定义类型的情况

当你显式调用方法时,Rust会自动触发Deref强制转换:因为&mut T实现了Deref<Target=T>,所以&mut Sample会被自动转换成&Sample,从而匹配我们为&Sample实现的add方法,这也是可变引用能替代不可变引用调用方法的原因。

但使用+运算符时,规则不一样:Rust会优先匹配左侧操作数的类型对应的Add实现,而且不会主动对左侧操作数执行Deref转换——只会对右侧操作数做自动类型转换。

  • 对于immut_b + mut_a:左侧是&Sample,刚好匹配我们实现的Add<&Sample>,右侧的&mut Sample会被自动转成&Sample,所以能运行。
  • 对于mut_a + immut_b:左侧是&mut Sample,我们没有为这个类型实现Add trait,Rust不会自动把它转成&Sample来匹配现有实现,因此报错。而&*mut_a是显式将可变引用转为不可变引用,自然能匹配上Add实现。

基础类型i32的情况

标准库为i32及其引用实现的Add trait,都是基于严格类型匹配的:

  • 显式调用mut_x.add(immut_y)时,&mut i32会通过Deref强制转换为&i32,从而调用&i32的Add<&i32>方法,所以能运行。
  • 但调用immut_y.add(mut_x)时,immut_y是&i32,它的add方法期望接收&i32参数,虽然&mut i32可以转成&i32,但显式调用方法时,Rust不会自动对参数做Deref转换(需要手动标注),因此报错。
  • 使用+运算符时,无论左右侧是可变还是不可变引用,因为标准库没有为&mut i32作为左侧操作数的Add实现,且Rust不会自动转换左侧类型,所以都会报错。

内容的提问来源于stack exchange,提问作者Edward Peters

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 19:00:08