我尝试理解Rust中的借用机制和引用,因此我创建了以下小例子:
extern crate core;
use core::fmt::Debug;
#[derive(Copy, Clone)]
pub struct Element(pub (crate) [u8; 5]);
impl Debug for Element {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "Element({:?})", &self.0[..])
}
}
impl Element {
fn one() -> Element {
Element([1, 1, 1, 1, 1])
}
fn double(&self) -> Element {
let mut result = *self;
for i in 0..5 { result.0[i] = 2*result.0[i]; }
result
}
fn mut_double(&mut self) -> Element {
for i in 0..5 { self.0[i] = 2*self.0[i]; }
*self
}
}
fn main() {
let mut a = Element::one();
println!("a = {:?}", a); // a = Element([1, 1, 1, 1, 1])
a = a.double();
println!("a = {:?}", a); // a = Element([2, 2, 2, 2, 2])
a = (&a).double();
println!("a = {:?}", a); // a = Element([4, 4, 4, 4, 4])
a = a.mut_double();
println!("a = {:?}", a); // a = Element([8, 8, 8, 8, 8])
a = (&mut a).mut_double();
println!("a = {:?}", a); // a = Element([16, 16, 16, 16, 16])
}
所以,上面的代码可以工作,但是在调用double
方法时我会感到困惑。正如您所看到的那样,它被定义为fn double(&self) -> Element
,因此它基本上采用了不可变的引用。现在主要是,我创建了一个名为Element
的新a
变量,然后在其上调用double
方法两次。我第一次做a.double()
,第二次(&a).double()
。它们似乎都正常工作,但我不明白为什么第一次调用a.double()
是有效的,编译器不会抱怨它。由于a
是Element
类型,而不是&Element
类型,显然double
方法要求&Element
,所以关于参考。 mut_double
方法也会发生同样的事情。为什么在分别调用(&a)
和(&mut a)
方法时,我不必指定double
或mut_double
?在Rust的引擎盖下发生了什么?
简短:语言就是这样,因为它更方便。
很长(extract from the book,重点是我的):
Where’s the
->
Operator?在像C ++这样的语言中,有两个不同的运算符用于调用方法:如果你直接在对象上调用方法,则使用
.
;如果你在指向对象的指针上调用方法并且需要首先取消引用指针,则使用->
。换句话说,如果object
是指针,object->something()
类似于(*object).something()
。Rust没有与
->
运算符等效的东西;相反,Rust有一个称为自动引用和解除引用的功能。调用方法是Rust中具有此行为的少数几个地方之一。以下是它的工作原理:当您使用
object.something()
调用方法时,Rust会自动添加&
,&mut
或*
,因此object
会匹配方法的签名。换句话说,以下是相同的:p1.distance(&p2); (&p1).distance(&p2);
第一个看起来更干净。这种自动引用行为是有效的,因为方法有一个明确的接收器 -
self
的类型。鉴于接收器和方法的名称,Rust可以明确地确定该方法是读取(&self
),变异(&mut self
)还是消费(self
)。 Rust为方法接收器隐含借用这一事实是在实践中使所有权符合人体工程学的重要部分。