Rust - Basic - 04 - Ownership
Ownership
Comprehension
在 Heap 的数据,let b = a;
只会浅拷贝,且此时 a
为 invalid,无法访问(即发生了 move)
需要写成 let b = a.clone();
才会深拷贝,此时 a
可以访问
如果函数输入参数类型声明为 String
则会发生浅拷贝(传入值 moved),而如果使用 reference (&String
)则不会 moved
(创建 reference 的过程叫 borrowing)
如果想要修改 reference 的值,入参需要声明为 &mut String
同一时刻,对同一源数据
只能有一个 &mut
reference 声明(可变),或者
能有多个 &
reference 声明(不可变)。
不允许:在 reference’s scope 范围内,既存在 let &b = a
又存在 let mut &c = a
,即无法同时 borrow 不可变 + 可变 reference
reference 编译时保证 valid,故不会存在 Dangling References
Rust 有 trait 这个 annotation,暂时不知道是什么
Origin
https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html
…
Rust has a special annotation called the Copy
trait that we can place on types like integers that are stored on the stack (we’ll talk more about traits in Chapter 10). If a type implements the Copy
trait, an older variable is still usable after assignment. Rust won’t let us annotate a type with the Copy
trait if the type, or any of its parts, has implemented the Drop
trait. If the type needs something special to happen when the value goes out of scope and we add the Copy
annotation to that type, we’ll get a compile-time error. To learn about how to add the Copy
annotation to your type to implement the trait, see “Derivable Traits” in Appendix C.
So what types implement the Copy
trait? You can check the documentation for the given type to be sure, but as a general rule, any group of simple scalar values can implement Copy
, and nothing that requires allocation or is some form of resource can implement Copy
. Here are some of the types that implement Copy
:
- All the integer types, such as
u32
. - The Boolean type,
bool
, with valuestrue
andfalse
. - All the floating point types, such as
f64
. - The character type,
char
. - Tuples, if they only contain types that also implement
Copy
. For example,(i32, i32)
implementsCopy
, but(i32, String)
does not.
…
Filename: src/main.rs
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
Here’s the error:
$ cargo run
Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0499]: cannot borrow `s` as mutable more than once at a time
--> src/main.rs:5:14
|
4 | let r1 = &mut s;
| ------ first mutable borrow occurs here
5 | let r2 = &mut s;
| ^^^^^^ second mutable borrow occurs here
6 |
7 | println!("{}, {}", r1, r2);
| -- first borrow later used here
For more information about this error, try `rustc --explain E0499`.
error: could not compile `ownership` due to previous error
This error says that this code is invalid because we cannot borrow s
as mutable more than once at a time. The first mutable borrow is in r1
and must last until it’s used in the println!
, but between the creation of that mutable reference and its usage, we tried to create another mutable reference in r2
that borrows the same data as r1
.
…
As always, we can use curly brackets to create a new scope, allowing for multiple mutable references, just not simultaneous ones:
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1 goes out of scope here, so we can make a new reference with no problems.
let r2 = &mut s;
A similar rule exists for combining mutable and immutable references. This code results in an error:
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM
println!("{}, {}, and {}", r1, r2, r3);
Here’s the error:
$ cargo run
Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--> src/main.rs:6:14
|
4 | let r1 = &s; // no problem
| -- immutable borrow occurs here
5 | let r2 = &s; // no problem
6 | let r3 = &mut s; // BIG PROBLEM
| ^^^^^^ mutable borrow occurs here
7 |
8 | println!("{}, {}, and {}", r1, r2, r3);
| -- immutable borrow later used here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership` due to previous error
Whew! We also cannot have a mutable reference while we have an immutable one. Users of an immutable reference don’t expect the values to suddenly change out from under them! However, multiple immutable references are okay because no one who is just reading the data has the ability to affect anyone else’s reading of the data.
Note that a reference’s scope starts from where it is introduced and continues through the last time that reference is used. For instance, this code will compile because the last usage of the immutable references, the println!
, occurs before the mutable reference is introduced:
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{} and {}", r1, r2);
// variables r1 and r2 will not be used after this point
let r3 = &mut s; // no problem
println!("{}", r3);
…
The Rules of References
Let’s recap what we’ve discussed about references:
- At any given time, you can have either one mutable reference or any number of immutable references.
- References must always be valid.