
之前我們了解 java、javascript 和 cpp 這些語言對內(nèi)存管理無非是由語言通過 GC 來管理內(nèi)存還是由 developer 來管理內(nèi)存,而今天 rust 給我們帶來一種的新的內(nèi)存管理方式。
rust 通過所有權(quán)機(jī)制來管理內(nèi)存,編譯器在編譯就會(huì)根據(jù)所有權(quán)規(guī)則對內(nèi)存的使用進(jìn)行檢查來回收內(nèi)存。
堆和棧
堆和棧是兩種數(shù)據(jù)結(jié)構(gòu),都是數(shù)據(jù)項(xiàng)按序排列的數(shù)據(jù)結(jié)構(gòu),棧是個(gè)特殊的存儲(chǔ) 區(qū),主要功能是暫時(shí)存放數(shù)據(jù)和地址,堆是隊(duì)列優(yōu)先,先進(jìn)先出(FIFO—first in first out)而棧是先進(jìn)后出(FILO—First-In/Last-Out)。

有關(guān)堆和棧的詳細(xì)信息,請參照詳解 JVM 的機(jī)制
作用域
有關(guān)作用域,作用域與變量相關(guān),也就是變量作用的范圍,通過劃分一定范圍,變量只在這個(gè)范圍(作用域)內(nèi)有效。
fn main() {
let x:i32 = 1;
let y:i32 = 2;
println!("x = {}",x);
println!("y = {}",y);
}
我們輸出 x 和 y 的值,當(dāng)我們用一對大括號(hào)(定義范圍)來將 y 變量括起來,在大括號(hào)內(nèi) y 是有效,當(dāng) y 離開大括號(hào)限定的作用域外,就被垃圾回收了。所以在定義 y 的大括號(hào)外我們無法訪問到 y 所有對應(yīng)變量的。
fn main() {
let x:i32 = 1;
{
let y:i32 = 2;
println!("x = {}",x);
}
println!("y = {}",y);
}
println!("y = {}",y);
| ^ help: a local variable with a similar name exists: `x`
表示 y 有效區(qū)域(作用域)只在作用域內(nèi)有效,出作用域就被回收。
fn main() {
let x:i32 = 1;
{
let y:i32 = 2;
println!("x = {}",x);
println!("y = {}",y);
}
}
對于指定確定類型的變量通常會(huì)被分配到棧內(nèi)存上,例如 x 和 y 已經(jīng)指定了類型 i32 所有分配的內(nèi)存大小也是固定所有分配在棧上。
fn main() {
let x:i32 = 1;
{
let y:i32 = 2;
println!("x = {}",x);
println!("y = {}",y);
}
{
let str = String::from("hello");
println!("str = {}",str);
}
}
這里 str 定義在堆上,String 類型字符串類型,編譯器是無法確定 Str 的大小的,例如我們可以通過 push_str來改變字符串。
fn main() {
let x:i32 = 1;
{
let y:i32 = 2;
println!("x = {}",x);
println!("y = {}",y);
}
{
let mut str = String::from("hello");
str.push_str(" world");
println!("str = {}",str);
}
}
fn main() {
let x:i32 = 1;
{
let y:i32 = 2;
println!("x = {}",x);
println!("y = {}",y);
}
{
let str1 = String::from("hello");
//str1.push_str(" world");
println!("str1 = {}",str1);
let str2 = str1;
println!("str2 = {}",str2);
println!("str1 = {}",str1);
}
}
通過上面代碼大家知道為什么 String 大小不是固定的了。所以編譯器將 str 分布
- prt 指針
- len 長度
- capacity 擴(kuò)展能力

在定義 str1 變量,變量 str1 保存一個(gè)指向內(nèi)存地址指針,也就是看成是內(nèi)存的引用。

在 cpp 語言我們通過將變量 str1 賦值給 str2,那么 str1 和 str2 保存指針指向同一個(gè)內(nèi)存地址,當(dāng) str1 和 str2 離開作用域就會(huì)被回收,但是因?yàn)樗麄兌贾赶蛲粔K內(nèi)存,所以這塊內(nèi)存會(huì)被釋放兩次所有發(fā)生錯(cuò)誤。

在 rust 當(dāng)將 str1 賦值給 str2 不再是 str1 和 str2 同時(shí)指向同一塊內(nèi)存,而是將 str1 對這塊內(nèi)存所有權(quán)將給了 str2。
所以在當(dāng) str1 將所有權(quán)交給了 str2 后再次打印 str1 就會(huì)報(bào)錯(cuò)。
println!("str1 = {}",str1);
| ^^^^ value borrowed here after move
這里所說 move 將所有權(quán)從 str1 移至 str2。
移動(dòng)
這里不得不說一下 rust 報(bào)錯(cuò)信息很準(zhǔn)確詳細(xì)的,這也是他能夠得到大家喜歡的一個(gè)原因吧。有點(diǎn)類似 cpp 的淺拷貝,也就是復(fù)制指針同時(shí)指向同一塊內(nèi)存。我們知道在 String 類型離開作用域時(shí)候會(huì)調(diào)用 drop 方法來釋放內(nèi)存,因?yàn)?str1 和 str2 都指向同一塊內(nèi)存所以當(dāng)離開作用域,他們指向內(nèi)存將會(huì)被釋放 2 次。所以 rust 語言在復(fù)制,move 后 str1 就無效,并不不是指向同一,所以... str1 是無效,所以發(fā)生上面報(bào)錯(cuò)。rust 通過move 指針來解決了上面內(nèi)存釋放兩次問題
fn main() {
let x:i32 = 1;
{
let y:i32 = 2;
println!("x = {}",x);
println!("y = {}",y);
}
{
let str1 = String::from("hello");
//str1.push_str(" world");
println!("str1 = {}",str1);
let str2 = str1;
println!("str2 = {}",str2);
// println!("str1 = {}",str1);
}
}
clone
fn main() {
let x:i32 = 1;
{
let y:i32 = 2;
println!("x = {}",x);
println!("y = {}",y);
}
{
let str1 = String::from("hello");
//str1.push_str(" world");
println!("str1 = {}",str1);
let str2 = str1;
println!("str2 = {}",str2);
// println!("str1 = {}",str1);
//clone
let str3 = str2.clone();
println!("str2 = {}",str2);
println!("str3 = {}",str3);
}
}
棧上數(shù)據(jù)拷貝
fn main() {
let a = 1;
let b = a;
println!("a = {}, b= {}",a,b);
}
在棧上變量直接拷貝,分配在棧上只有就是clone行為,叫 copy 的特征。只要類型實(shí)現(xiàn) copy 特征,在賦值其他變量依然可以使用,常用具有 copy 特征類型,所有整型、浮點(diǎn)型、布爾型、字符類型和元組。
函數(shù)和作用域
fn take_ownership(str:String){
println!("{}",str);
}
fn make_copy(a:i32){
println!("a = {}",a)
}
fn main() {
let str1 = String::from("hello");
take_ownership(str1);
let x = 5;
make_copy(x);
}
fn main() {
let str1 = String::from("hello");
take_ownership(str1);
println!("{}",str1);
let x = 5;
make_copy(x);
}
fn main() {
let str1 = String::from("hello");
take_ownership(str1);
// println!("{}",str1);
let x = 5;
make_copy(x);
println!("x = {}",x);
}
fn take_ownership(str:String)->String{
println!("{}",str);
str
}
fn main() {
let str1 = String::from("hello");
let str2 = take_ownership(str1);
println!("{}",str2);
let x = 5;
make_copy(x);
println!("x = {}",x);
}