AddAssign 和 '+=' 运算符之间的关系
Relation between AddAssign and '+=' Operator
到目前为止,我的理解是,在 Rust 中,运算符基本上是特征方法调用的语法糖。特别是,我认为 a += b
等同于写作 a.add_assign(b)
。今天我很惊讶地听到 rustc (1.44.1) 的以下内容:
error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut u8`
--> src/main.rs:2:5
|
2 | a += b;
| -^^^^^
| |
| cannot use `+=` on type `&mut u8`
|
help: `+=` can be used on 'u8', you can dereference `a`
|
2 | *a += b;
| ^^
导致错误消息的代码是 (Playground)
fn test_add_assign(a: &mut u8, b: u8) {
a += b;
}
fn main() {
let mut test = 1;
test_add_assign(&mut test, 1);
assert_eq!(test, 2);
}
现在,编译器是正确的,编写 *a += b
可以工作,并且还可以正确地将新变量分配给 a。然而,令我惊讶的是, a.add_assign(b)
也可以正常工作而无需取消引用 a
(Playground):
fn test_add_assign(a: &mut u8, b: u8) {
a.add_assign(b);
}
考虑到 AddAssign
的文档只是说明
The addition assignment operator +=
.
我在想:AddAssign
和+=
运算符是什么关系,如果不是调用trait方法的基本语法糖?
你或多或少是对的。
我认为让您感到困惑的问题是方法函数调用中的自动取消引用。它在 中有详细说明,但基本上它说您可以使用值或引用或对引用的引用来调用成员函数,并且它会起作用:
let x = 42;
let _ = x.to_string(); //ok
let _ = (&x).to_string(); //ok
let r = &x;
let _ = r.to_string(); //ok
let _ = (*r).to_string(); //ok
但是当使用运算符时,自动 deref 不适用。所以:
let mut x = 42;
x += 1; //ok;
x.add_assign(1); //ok
let r: &mut i32 = &mut x;
*r += 1; //ok
r += 1; //error: &mut i32 does not implement AddAssign
r.add_assign(1); //ok: r is auto-dereffed
请注意 +=
的左侧表达式必须是要修改的值(右值),而不是对该值的引用。然后,实际上当你写 a += b
它相当于 AddAssign::add_assign(&mut a, b)
I thought that a += b
was equivalent to writing a.add_assign(b)
.
不完全是,a += b
实际上是翻译成::std::ops::AddAssign::add_assign(&mut a, b)
。在您的示例中,这意味着您将传递 &mut &mut u8
作为第一个参数。
如果你仔细想想,这是有道理的。整数变量 i
的标准赋值写为 i = 3;
。如果您想改为调用函数,则需要将 i
的可变引用传递给该函数,以便它实际上可以修改 i
的值。这同样适用于扩充作业。
请注意,方法调用语法 a.add_assign(b)
恰好适用于这种情况,因为 method calls treat the receiver in a special way。编译器通过隐式借用和取消引用接收器来查找匹配方法,直到找到匹配项。对带有类型参数的特征的方法调用又是特殊的,因为搜索甚至可能会继续为该方法找到 other 参数的匹配项(我认为没有记录此时在 Rust 参考中)。
到目前为止,我的理解是,在 Rust 中,运算符基本上是特征方法调用的语法糖。特别是,我认为 a += b
等同于写作 a.add_assign(b)
。今天我很惊讶地听到 rustc (1.44.1) 的以下内容:
error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut u8`
--> src/main.rs:2:5
|
2 | a += b;
| -^^^^^
| |
| cannot use `+=` on type `&mut u8`
|
help: `+=` can be used on 'u8', you can dereference `a`
|
2 | *a += b;
| ^^
导致错误消息的代码是 (Playground)
fn test_add_assign(a: &mut u8, b: u8) {
a += b;
}
fn main() {
let mut test = 1;
test_add_assign(&mut test, 1);
assert_eq!(test, 2);
}
现在,编译器是正确的,编写 *a += b
可以工作,并且还可以正确地将新变量分配给 a。然而,令我惊讶的是, a.add_assign(b)
也可以正常工作而无需取消引用 a
(Playground):
fn test_add_assign(a: &mut u8, b: u8) {
a.add_assign(b);
}
考虑到 AddAssign
的文档只是说明
The addition assignment operator
+=
.
我在想:AddAssign
和+=
运算符是什么关系,如果不是调用trait方法的基本语法糖?
你或多或少是对的。
我认为让您感到困惑的问题是方法函数调用中的自动取消引用。它在
let x = 42;
let _ = x.to_string(); //ok
let _ = (&x).to_string(); //ok
let r = &x;
let _ = r.to_string(); //ok
let _ = (*r).to_string(); //ok
但是当使用运算符时,自动 deref 不适用。所以:
let mut x = 42;
x += 1; //ok;
x.add_assign(1); //ok
let r: &mut i32 = &mut x;
*r += 1; //ok
r += 1; //error: &mut i32 does not implement AddAssign
r.add_assign(1); //ok: r is auto-dereffed
请注意 +=
的左侧表达式必须是要修改的值(右值),而不是对该值的引用。然后,实际上当你写 a += b
它相当于 AddAssign::add_assign(&mut a, b)
I thought that
a += b
was equivalent to writinga.add_assign(b)
.
不完全是,a += b
实际上是翻译成::std::ops::AddAssign::add_assign(&mut a, b)
。在您的示例中,这意味着您将传递 &mut &mut u8
作为第一个参数。
如果你仔细想想,这是有道理的。整数变量 i
的标准赋值写为 i = 3;
。如果您想改为调用函数,则需要将 i
的可变引用传递给该函数,以便它实际上可以修改 i
的值。这同样适用于扩充作业。
请注意,方法调用语法 a.add_assign(b)
恰好适用于这种情况,因为 method calls treat the receiver in a special way。编译器通过隐式借用和取消引用接收器来查找匹配方法,直到找到匹配项。对带有类型参数的特征的方法调用又是特殊的,因为搜索甚至可能会继续为该方法找到 other 参数的匹配项(我认为没有记录此时在 Rust 参考中)。