Rust基礎02-所有權規則、記憶體與分配
所有權規則
- 每個值都有一個變數,這個變數是該值的所有者
- 每個值同時只能有一個所有者
- 當所有者超出作用域(scope)時,該值將被刪除
變數作用域
Scope 就是程式中一個專案的有效範圍
fn main() {
//s 不可用
let s = "hello";//s 可用
//可對 s 進行相關操作
} //s 作用域到此結束,s 不再可用
String 型別
字串字面值:程式裡手寫的那些字串,它們是不可變的
String型別:在Heap上分配,能夠儲存在編譯時未知數量的文字
-
建立 String型別 值
可以使用 from 函式從字串字面值創建出 String 型別
fn main() { let s =String::from("hello"); }
其中,"::" 表示 from 是 String 型別下的函式,這類字串是可以被修改的
String型別能夠修改,字串字面量不能修改,這是因為它們的儲存方式不同
記憶體和分配
-
字串字面值:
在編譯時就知道它的內容,其文字內容直接被硬編碼到最終的可執行檔案中
速度快、高效、不可變性
-
String 型別:
支援可變性,在Heap上分配記憶體來儲存編譯時未知的文字內容
-
作業系統必須在執行時請求記憶體
此步通過呼叫 String::from 來實現
-
當用完 String 後,需要使用某種方式將記憶體返回給作業系統
但Rust採用了不同的方式:對於某個值來說,當擁有它的變數走出作用範圍時,記憶體會立即自動的交還給作業系統
當變數走出作用域,Rust會自動呼叫 drop 函式
-
-
移動(Move):
-
普通情況:
多個變數可以於同一個資料使用一種獨特的方式來互動
let x = 5; let y = x;
整數是已知且固定大小的簡單的值,這兩個5被壓入Stack中
-
String 版本:
let s1 = String::from("hello"); let s2 = s1;
一個String由三部分組成:
-
一個指向存放字串內容的記憶體的指標
-
一個長度:
長度len,就是存放字串內容所需的位元組數
-
一個容量:
容量capacity,指String從作業系統總共獲得記憶體的總位元組數
以上三部分放在 Stack 上,存放字串內容的部分在 Heap 上
當把 s1 賦給 s2,String 的資料被複制了一份:
-
在 Stack 上覆制了一份指標、長度、容量
-
並沒有複製指標所指向的 Heap 上的資料
當變數離開作用域時,Rust 會自動呼叫 drop 函式,並將變數使用的 Heap 記憶體釋放
當s1、s2離開作用域時,它們都會嘗試釋放相同的記憶體:
這會引起嚴重的BUG:二次釋放(double free)bug
因此,Rust為了保證記憶體安全:
-
Rust 沒有嘗試複製被分配的記憶體
-
Rust 讓 s1 失效,當 s1 離開作用域時,Rust不需要釋放任何東西
引申:淺拷貝與深拷貝
-
-
-
克隆(Clone):
若有意對 Heap 上的 String 資料進行深度拷貝,而不僅僅是 Stack 上的資料,可以使用 clone 方法
這種方法較為耗費資源且針對 Heap 上的資料進行操作
-
複製(Copy):對 Stack 上的資料進行操作
Copy trait,可以用於像整數這樣完全存放在 Stack 上的型別
-
如果一個型別實現了 Copy 這個 trait,那麼舊的變數在賦值後仍然可用
-
但若一個型別或該型別的一部分已實現了 Drop trait,那麼 Rust 不允許讓它再去實現 Copy trait 了
任何簡單標量的組合型別都可以是 Copy 的,任何需要分配記憶體或某種資源的都不是 Copy 的
一些擁有 Copy trait 的型別:
- 所有的整數型別,例如 u32
- 布林型別
- 字元型別
- 所有的浮點型別,例如 f64
- 元組,如果其所有的欄位都是 Copy 的
-