可变引用
如果您想使用一个引用来改变数据,您可以使用一个可变引用。对于可变引用,您可以写 &mut
而不是 &
。
fn main() { let mut my_number = 8; // don't forget to write mut here! let num_ref = &mut my_number; }
那么这两种类型是什么呢?my_number
是i32
,num_ref
是&mut i32
(我们说是 "可变引用i32
")。
所以我们用它来给my_number加10。但是你不能写num_ref += 10
,因为num_ref
不是i32
的值,它是一个&i32
。其实这个值就在i32
里面。为了达到值所在的地方,我们用*
。*
的意思是 "我不要引用,我要引用对应的值"。换句话说,一个*
与&
是相反的。另外,一个*
抹去了一个&
。
fn main() { let mut my_number = 8; let num_ref = &mut my_number; *num_ref += 10; // Use * to change the i32 value. println!("{}", my_number); let second_number = 800; let triple_reference = &&&second_number; println!("Second_number = triple_reference? {}", second_number == ***triple_reference); }
这个打印:
18
Second_number = triple_reference? true
因为使用&
叫做 "引用",所以使用*
叫做 "dereferencing"。
Rust有两个规则,分别是可变引用和不可变引用。它们非常重要,但也很容易记住,因为它们很有意义。
- 规则1。如果你只有不可变引用,你可以有任意多的引用。1个也行,3个也行,1000个也行,没问题。
- 规则2: 如果你有一个可变引用,你只能有一个。另外,你不能同时使用一个不可变引用和一个可变引用。
这是因为可变引用可以改变数据。如果你在其他引用读取数据时改变数据,你可能会遇到问题。
一个很好的理解方式是思考一个Powerpoint演示。
情况一是关于只有一个可变引用
情境一 一个员工正在编写一个Powerpoint演示文稿,他希望他的经理能帮助他。他希望他的经理能帮助他。该员工将自己的登录信息提供给经理,并请他帮忙进行编辑。现在,经理对该员工的演示文稿有了一个 "可变引用"。经理可以做任何他想做的修改,然后把电脑还给他。这很好,因为没有人在看这个演示文稿。
情况二是关于只有不可变引用
情况二 该员工要给100个人做演示。现在这100个人都可以看到该员工的数据。 他们都有一个 "不可改变的引用",即员工的介绍。这很好,因为他们可以看到它,但没有人可以改变数据。
情况三是有问题的情况
情况三 员工把他的登录信息给了经理 他的经理现在有了一个 "可变引用"。然后员工去给100个人做演示,但是经理还是可以登录。这是不对的,因为经理可以登录,可以做任何事情。也许他的经理会登录电脑,然后开始给他的母亲打一封邮件! 现在这100人不得不看着经理给他母亲写邮件,而不是演示。这不是他们期望看到的。
下面是一个可变借用与不可变借用的例子:
fn main() { let mut number = 10; let number_ref = &number; let number_change = &mut number; *number_change += 10; println!("{}", number_ref); // ⚠️ }
编译器打印了一个有用的信息来告诉我们问题所在。
error[E0502]: cannot borrow `number` as mutable because it is also borrowed as immutable
--> src\main.rs:4:25
|
3 | let number_ref = &number;
| ------- immutable borrow occurs here
4 | let number_change = &mut number;
| ^^^^^^^^^^^ mutable borrow occurs here
5 | *number_change += 10;
6 | println!("{}", number_ref);
| ---------- immutable borrow later used here
然而,这段代码可以工作。为什么会这样?
fn main() { let mut number = 10; let number_change = &mut number; // create a mutable reference *number_change += 10; // use mutable reference to add 10 let number_ref = &number; // create an immutable reference println!("{}", number_ref); // print the immutable reference }
它打印出20
没有问题。它能工作是因为编译器足够聪明,能够理解我们的代码。它知道我们使用了number_change
来改变number
,但没有再使用它。所以这里没有问题。我们并没有将不可变和可变引用一起使用。
早期在Rust中,这种代码实际上会产生错误,但现在的编译器更聪明了。它不仅能理解我们输入的内容,还能理解我们如何使用所有的东西。
再谈shadowing
还记得我们说过,shadowing不会破坏一个值,而是屏蔽它吗?现在我们可以用引用来看看这个问题。
fn main() { let country = String::from("Austria"); let country_ref = &country; let country = 8; println!("{}, {}", country_ref, country); }
这是打印Austria, 8
还是8, 8
?它打印的是Austria, 8
。首先我们声明一个String
,叫做country
。然后我们给这个字符串创建一个引用country_ref
。然后我们用8来shadowing国家,这是一个i32
。但是第一个country
并没有被销毁,所以country_ref
仍然写着 "Austria",而不是 "8"。下面是同样的代码,并加了一些注释来说明它的工作原理。
fn main() { let country = String::from("Austria"); // Now we have a String called country let country_ref = &country; // country_ref is a reference to this data. It's not going to change let country = 8; // Now we have a variable called country that is an i8. But it has no relation to the other one, or to country_ref println!("{}, {}", country_ref, country); // country_ref still refers to the data of String::from("Austria") that we gave it. }