所有權(quán)指涵蓋的范圍有: 變量作用域、垃圾處理機(jī)制、引用,圍繞著幾種行為來闡述所有權(quán)概念。
?
變量作用域(Scope)
任何編程語言中都存在變量作用域的概念, Rust中的變量作用域與其他編程語言也基本一致;作用域大致上會分為: 全局作用域, 函數(shù)內(nèi)作用域.
全局作用域
在下面這段代碼中, PROGRAM常量和main函數(shù)就是一個全局作用域; 反過來講就是當(dāng)前程序代碼中,全局作用域擁有兩個對象, 第一個對象是 PROGRAM 常量, 第二個對象是 main 函數(shù)。
const PROGRAM: &'static str = "Rust";
fn main() {
println!("{}", PROGRAM);
}
# 作用域的形式可以這么理解, global_scope中有兩個對象.
global_scope = {
"const PROGRAM": "Rust"
"fn main": "println!({}, PROGRAM)"
}
函數(shù)內(nèi)作用域
變量不能let(定義)在全局作用域中, 所以全局作用域能做的事情并不多: 定義常量、定義函數(shù)、引用其他模塊文件; 更多的合法操作實在函數(shù)作用域中完成: 定義變量、打印變量、邏輯計算、邏輯判斷、函數(shù)調(diào)用和執(zhí)行等.
const PROGRAM: &'static str = "Rust";
fn main() {
let s = "abc"; // 定義變量
let sf = simple_function(); // 執(zhí)行函數(shù)
let sum = 10 + 15; // 邏輯計算
if sum > 0 { // 邏輯判斷
println!("{} {} {}", PROGRAM, s, sum); // 打印常量和變量
}
}
fn simple_function() {
let sf = "Simple Function"; // 定義變量
sf // return 變量
}
# 作用域的形式可以這么理解, global_scope中有三個對象, 其中
# main對象中又有一個二級作用域, simple_function對象中也有一個
# 二級作用域.
global_scope = { // 作用域
"const PROGRAM": "Rust",
"main": { // 作用域
"let s": "abc",
"let sf": "Simple Function",
"let sum": 25,
},
"simple_function": { // 作用域
"let sf": "Simple Function"
}
}
?
?
垃圾處理機(jī)制
像其他具備GC回收機(jī)制編程語言一樣,當(dāng)程序執(zhí)行完成并跳出某個作用域(通常指的是一個函數(shù))時,該作用域中的所有變量將會失效(被回收);除此之外, Rust對垃圾回收這件事情上還具備其他的能力和行為, 例如: 當(dāng)需要對存儲在堆(Heap)中的數(shù)據(jù)進(jìn)行復(fù)制時, 被賦值對象將具備賦值對象的數(shù)據(jù),而賦值對象將會被回收。
離開作用域時回收數(shù)據(jù)
fn simple_function() {
let sf = "Simple Function"; // sf變量開始生效
println!("{}", sf); // sf變量仍然生效
} // sf變量不再生效
fn main() {
simple_function(); // 當(dāng)該函數(shù)執(zhí)行完成后, sf變量就會被回收
println!("{}", sf); // 報錯: sf變量不存在.
}
變量傳遞后即刻失效
fn simple_function(sf: String) {
println!("simple_function: {}", sf);
}
fn main() {
let s = String::from("hello");
simple_function(s);
println!("main: {}", s);
}
# 報錯
Compiling ownership v0.1.0 (file:///opt/learn_rust/ownership)
error[E0382]: use of moved value: `s`
--> src/main.rs:10:26
|
9 | simple_function(s);
| - value moved here
10 | println!("main: {}", s);
| ^ value used here after move
|
= note: move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait
通過return讓它重回原作用域
fn simple_function(sf: String) -> String { // -> String 聲明返回值類型
println!("simple_function: {}", sf);
sf
}
fn main() {
let s = String::from("hello");
let s = simple_function(s); // 利用返回值, 重新定義變量.
println!("main: {:?}", s)
}
數(shù)據(jù)存儲在棧上時, 變量再賦值不會回收原變量
fn main() {
let a = 10; // rust會將固定不變的值存儲在棧(Stack)上.
let b = a; // rust對棧上的數(shù)據(jù)默認(rèn)采取深復(fù)制.
println!("{} {}", a, b);
}
fn main() {
let a = "abc"; // 與上面是一樣的.
let b = a;
println!("{} {}", a, b);
}
當(dāng)變量存儲再堆上時, 變量再賦值就會回收原變量(所有權(quán)轉(zhuǎn)移)
fn main() {
// rust會將這種未知大小的數(shù)據(jù)存儲在堆(Heap)上.
// 因為String::from這種結(jié)構(gòu)的數(shù)據(jù)支持push_str
// 來擴(kuò)充它的大小, 因此從本質(zhì)上來講是未知大小.
let a = String::from("hello");
// rust對堆上的數(shù)據(jù)默認(rèn)采取移除上一個變量創(chuàng)建
// 新變量的機(jī)制, 這種做法在術(shù)語上叫做所有權(quán)轉(zhuǎn)移.
let b = a;
// 這里會報錯, a變量已被移除
println!("{} {}", a, b)
}
# 報錯信息
Compiling ownership v0.1.0 (file:///opt/learn_rust/ownership)
error[E0382]: use of moved value: `a`
--> src/main.rs:10:23
|
7 | let b = a;
| - value moved here
...
10 | println!("{} {}", a, b)
| ^ value used here after move
|
= note: move occurs because `a` has type `std::string::String`, which does not implement the `Copy` trait
?
?
引用(reference)
不可變更引用(默認(rèn))
fn main() {
let a = 10;
// b變量是一個指針, 它并沒有實際數(shù)據(jù)的所有權(quán).
let b = &a;
// 引用是在棧上創(chuàng)建一個指針指向棧數(shù)據(jù).
// 它比在棧上深復(fù)制更輕量.
println!("{} {}", a, b)
}
可變引用
fn main() {
let mut a = "hello";
a + "b"
println!("{}", a)
}
引用的注意事項
可變對象不能被多次引用, 這會導(dǎo)致數(shù)據(jù)競爭.
fn main() {
let mut a = 10;
let b = &mut a;
let c = &mut a;
}
# 報錯
Compiling ownership v0.1.0 (file:///opt/learn_rust/ownership)
error[E0499]: cannot borrow `a` as mutable more than once at a time
--> src/main.rs:18:18
|
17 | let b = &mut a;
| - first mutable borrow occurs here
18 | let c = &mut a;
| ^ second mutable borrow occurs here
19 | }
可變對象被可變引用之后, 再次引用會導(dǎo)致數(shù)據(jù)不一致.
fn main() {
let mut a = 10;
// 可變對象被可變引用走了
let b = &mut a;
// 這里會報錯, 因為數(shù)據(jù)狀態(tài)任何時刻都可能會改變,
// 這是不可預(yù)期的, 所以rust不允許這種情況的出現(xiàn).
println!("{} {}", a, b)
}
可變對象被可變引用之后,數(shù)據(jù)不一致的解決辦法
fn simple_function(sf: &mut String) -> String {
sf.push_str(" world!");
let new_sf = sf.clone();
new_sf
}
fn main() {
let mut a = String::from("hello");
// 站在變量的角度來講: &mut a 的專業(yè)術(shù)語為<可變引用>
// &mut a 當(dāng)做參數(shù)傳遞給函數(shù)時, 專業(yè)術(shù)語為<可變借用>
let b = simple_function(&mut a);
println!("a: {}\nb: {}", a, b)
}