頭條地址:https://www.ixigua.com/i676544267458235648...
B站地址:https://www.bilibili.com/video/av78062009?...
網易雲課堂地址:https://study.163.com/course/introduction....
-
所有權是rust語言中最與眾不同的特性,它讓rust無需垃圾回收就可以保證記憶體安全。
-
計算機語言對記憶體的管理有三種方式:
- 程式設計師自己分配和回收,例如:c/c++。
- 語言自帶GC,例如go語言。
- rust語言為第三種方式,通過所有權系統管理記憶體,編譯器在編譯時根據規則對記憶體使用進行檢查。
3. 棧和堆
- 棧的操作是先進先出,儲存於棧上的資料編譯時都佔用已知固定的大小。分配方式:直接分配
- 編譯時大小未知或者是變化的資料在堆上進行儲存。 表示方式:用指標和大小表示
4. 變數作用域
一對花括號表示的範圍,就是作用域,如下:
{
let i = 1; --------------------------------|
... |
let j = 2; -------| |---i的作用域
... |------j的作用域 |
...----------------|--------------------------|
}
說明:
- 變數進入它的作用域有效
- 離開作用域後無效
- 對於String型別,如:
let s = String::from("hello"); s.push_str(", world");
對於s,其所需要記憶體大小是變化的,因此需要在堆上進行分配。
對於在堆上分配的記憶體,程式語言一般有幾種回收方式:- 有GC的語言自動回收,如go、java
- 程式設計師手動回收,如c/c++
- rust語言採用:當變數離開它的作用域後,就自動釋放。
因此,在c語言中:
{
let s = String::from("hello");
...
} //自動釋放s,s不再有效
這是一個將 String 需要的記憶體返回給作業系統的很自然的位置:當 s 離開作用域的時候。當變數離開作用域,Rust 為我們呼叫一個特殊的函式。這個函式叫做 drop,在這裡 String 的作者可以放置釋放記憶體的程式碼。Rust 在結尾的 } 處自動呼叫 drop。
- 移動
例子1:let x = 5; //將5繫結到x let y = x; //生成一個x的拷貝繫結到y
由於都是確定的型別,所以在存放在棧中。
例子2:
let s1 = String::from("hello"); //s1在堆上
let s2 = s1; //移動s1到s2,此後不能再使用s1
println!("{}, world!", s1); //此處要報錯,s1不能再使用
原因:
- 兩者的存放方式,畫圖 是淺拷貝
- 如果s1繼續有效,那麼釋放的時候(呼叫drop)會釋放兩次,會發生錯誤,因此這裡s1不能再使用
說明:rust語言預設是淺拷貝,同時將被拷貝的變數就會變得無效。
-
克隆
如果要複製堆上的內容,則使用clone,例子:let s1 = String::from("hello"); let s2 = s1.clone(); println!("s1 = {}, s2 = {}", s1, s2);
-
在棧上的資料拷貝
例如:let x = 5; let y = x; println!("x = {}, y = {}", x, y); //ok,x,y都在棧上
Rust 有一個叫做 Copy trait 的特殊註解,可以用在類似整型這樣的儲存在棧上的型別上。如果一個型別擁有 Copy trait,一箇舊的變數在將其賦值給其他變數後仍然可用。Rust 不允許自身或其任何部分實現了 Drop trait 的型別使用 Copy trait。
常用的具有Copy trait的有:
- 所有整數型別,比如 u32。
- 布林型別,bool,它的值是 true 和 false。
- 所有浮點數型別,比如 f64。
- 字元型別,char。
- 元組,當且僅當其包含的型別也都是 Copy 的時候。比如,(i32, i32) 是 Copy 的,但 (i32, String) 就不是。
- 函式與作用域
例子1:fn main() { let s = String::from("hello"); // s 進入作用域 takes_ownership(s); // s 的值移動到函式裡 ... // ... 所以到這裡不再有效 let x = 5; // x 進入作用域 makes_copy(x); // x 應該移動函式裡, // 但 i32 是 Copy 的,所以在後面可繼續使用 x } // 這裡, x 先移出了作用域,然後是 s。但因為 s 的值已被移走,所以不會有特殊操作
fn takes_ownership(some_string: String) { // some_string 進入作用域 println!("{}", some_string); } // 這裡,some_string 移出作用域並呼叫 `drop` 方法。佔用的記憶體被釋放
fn makes_copy(some_integer: i32) { // some_integer 進入作用域 println!("{}", some_integer); } // 這裡,some_integer 移出作用域。不會有特殊操作
例子2:
fn main() {
let s1 = gives_ownership(); // gives_ownership 將返回值,移給 s1
let s2 = String::from("hello"); // s2 進入作用域
let s3 = takes_and_gives_back(s2); // s2 被移動到takes_and_gives_back 中, 它也將返回值移給 s3
} // 這裡, s3 移出作用域並被丟棄。s2 也移出作用域,但已被移走,所以什麼也不會發生。s1 移出作用域並被丟棄
fn gives_ownership() -> String { // gives_ownership 將返回值移動給呼叫它的函式
let some_string = String::from("hello"); // some_string 進入作用域.
some_string // 返回 some_string 並移出給呼叫的函式
}
// takes_and_gives_back 將傳入字串並返回該值
fn takes_and_gives_back(a_string: String) -> String { // a_string 進入作用域
a_string // 返回 a_string 並移出給呼叫的函式
}
總結: 將值賦給另一個變數時移動它。當持有堆中資料值的變數離開作用域時,其值將通過 drop 被清理掉,除非資料被移動為另一個變數所有。
本作品採用《CC 協議》,轉載必須註明作者和本文連結