第五章 Rust 所有權(quán)

一、棧內(nèi)存與堆內(nèi)存

棧內(nèi)存用于存儲固定大小的數(shù)據(jù),速度快。
堆內(nèi)存用于存儲動態(tài)大小的數(shù)據(jù),靈活性高。

Rust 中的內(nèi)存管理主要涉及棧內(nèi)存和堆內(nèi)存,這兩種內(nèi)存的使用方式不同,適用于不同的場景。

  • 棧內(nèi)存:存儲大小固定的數(shù)據(jù),分配和釋放速度非??臁?nèi)存遵循“后進先出”的原則,就像疊盤子一樣,放盤子和取盤子都只能從頂部進行。例子:基礎(chǔ)類型(如 i32、char、f64)存儲在棧內(nèi)存中。
  • 堆內(nèi)存:存儲大小動態(tài)變化的數(shù)據(jù),分配和釋放速度較慢,但更靈活。需要先找到一塊足夠大的空間,然后返回一個指向該空間的指針。例子:動態(tài)字符串(String 類型)存儲在堆內(nèi)存中。

二、動態(tài)字符串與內(nèi)存管理

Rust 中的 String 類型是一個動態(tài)字符串,它允許在運行時動態(tài)管理堆內(nèi)存中的數(shù)據(jù),比如分配、增長和修改字符串內(nèi)容。

// 創(chuàng)建動態(tài)字符串
let s1 = String::from("hello"); // 在堆內(nèi)存中分配空間存儲 "hello",此時,s1 在棧內(nèi)存中存儲了堆內(nèi)存的指針、字符串長度和容量信息。

// 克隆(深拷貝)
let s3 = s1.clone(); // 在堆內(nèi)存中復(fù)制一份數(shù)據(jù),s3 指向新的內(nèi)存空間
println!("s1 = {}, s3 = {}", s1, s3); // s1 和 s3 是兩個獨立的數(shù)據(jù)

注意:

  1. 如果需要對字符串進行修改而不影響原字符串,可以使用 clone 方法進行深拷貝:
  2. 克隆操作會復(fù)制堆內(nèi)存中的數(shù)據(jù),因此對性能有一定影響,尤其是處理大數(shù)據(jù)或頻繁操作時。

三、所有權(quán)機制

Rust 的所有權(quán)機制是其內(nèi)存安全的核心,確保程序在運行時不發(fā)生數(shù)據(jù)競爭、懸垂指針等內(nèi)存安全問題。

所有權(quán)三原則

  • 每個值都有一個所有者(變量)
  • 一個值同時只能有一個所有者
  • 當所有者離開作用域時,值會被自動釋放(drop)。

所有權(quán)轉(zhuǎn)移(Move)

當值被賦值給另一個變量時,所有權(quán)會轉(zhuǎn)移,原變量將無法再使用該值。

在其他語言(如 Java)中,淺拷貝只復(fù)制棧內(nèi)存中的指針,多個變量共享同一塊堆內(nèi)存數(shù)據(jù)。這種方式效率高,但容易引發(fā)數(shù)據(jù)競爭問題。

Rust 通過所有權(quán)機制避免了淺拷貝帶來的問題:

  • 所有權(quán)轉(zhuǎn)移時,只進行“淺拷貝”(復(fù)制棧內(nèi)存中的指針信息),但原變量會失去所有權(quán)。
  • 新變量接管所有權(quán)后,原變量失效,確保每個值只有一個清晰定義的所有者。
let s1 = String::from("hello");
let s2 = s1; // 所有權(quán)從 s1 轉(zhuǎn)移到 s2
// println!("{}", s1); // 錯誤:s1 不再擁有數(shù)據(jù)
println!("{}", s2); // 正確:s2 擁有數(shù)據(jù)

函數(shù)形式的轉(zhuǎn)移:(注意所有權(quán)的轉(zhuǎn)移和借用的不同

fn main() {
    /* 所有權(quán)轉(zhuǎn)移 */
    let my_book = String::from("Rust Programming");
    // my_book 的所有權(quán)被移交至 give_book 函數(shù)
    give_book(my_book);
    // 此后 my_book 便無法再被使用
    // println!("my_book: {}", my_book); // 編譯出錯

    /* 所有權(quán)借用 */
    let my_book = String::from("Rust Programming");
    // my_book 的所有權(quán)被移交至 give_book 函數(shù)
    borrow_book(&my_book);
    // 此后 my_book 便無法再被使用
    println!("my_book: {}", my_book); // 編譯出錯
}

// 所有權(quán)轉(zhuǎn)移
fn give_book(book: String) {
    println!("Given book: {}", book);
    // book 在這里被 dropped 釋放
}

// 所有權(quán)借用
fn borrow_book(book: &String) {
    println!("borrow book: {}", book);
    // book 在這里被 dropped 釋放
}

作用域與內(nèi)存釋放

變量的有效范圍從聲明的地方開始,直到當前作用域結(jié)束。當變量離開作用域時,其占用的內(nèi)存會被自動釋放。

{
    let s1 = String::from("hello"); // s1 進入作用域
} // s1 離開作用域,內(nèi)存被釋放

四、借用與解引用

基礎(chǔ)

fn main() {
    // 定義變量
    let my_book = String::from("Rust Programming");
    // 借用,語法:&變量
    let borrowed_book = &my_book;
    // 解引用(讀取變量內(nèi)容),語法:*引用
    println!("my_book:{}, book_content:{}", my_book, *borrowed_book)
}

不可變引用與可變引用

借用分為兩種:不可變引用和可變引用。不可變引用就像你借給朋友的書,他只能讀,不能寫:

fn main() {
    // 定義變量
    let my_book = String::from("Rust Programming");
    // 借用,語法:&變量 - 不可變借用;&mut 變量 - 可變借用
    let borrowed_book = &my_book;
    // 本身還是保留了訪問權(quán)限
    println!("my_book:{},", my_book);
    // 解引用(讀取變量內(nèi)容),語法:*引用
    println!("book_content:{}", *borrowed_book);
    // 不可變借用 - 只讀
    println!("read_book:{}", read_book(borrowed_book));
    // 不可變借用不可以寫
    // write_in_book(borrowed_book); // 編譯出錯,不可變不可以寫
    
    // &mut 變量 - 可變借用
    let mut mut_my_book = String::from("Rust Programming");
    let mut_borrowed_book: &mut String = &mut mut_my_book;
    println!("read_book2:{}", read_book(mut_borrowed_book));
    write_in_book(mut_borrowed_book);
    println!("read_book3:{}", read_book(mut_borrowed_book));
}

// 讀
fn read_book(book: &String) -> usize {
    book.len()
}

// 寫
fn write_in_book(book: &mut String) {
    book.push_str(" - Notes by friend");
}

懸垂引用

懸垂引用(Dangling Reference)是指一個引用指向了已經(jīng)被釋放的內(nèi)存
Rust 編譯器會在編譯期自行檢查該問題

fn main() {
    let tenant;
    {
        let house = String::from("Apartment 101");
        tenant = &house; // house 的作用域結(jié)束了,但 tenant 還在引用它
    }
    println!("Tenant lives in: {}", tenant); // 編譯失?。篽ouse 已經(jīng)被釋放
}

分級釋放

釋放結(jié)構(gòu)體時,Rust 會先釋放父結(jié)構(gòu)體,再釋放子結(jié)構(gòu)體:

struct Chapter {
    content: String,
}

struct Book {
    title: String,
    chapter: Chapter,
}

fn main() {
    let my_book = Book {
        title: String::from("Rust Programming"),
        chapter: Chapter {
            content: String::from("Chapter 1: Ownership"),
        },
    };
    println!("Book: {}, Chapter: {}", my_book.title, my_book.chapter.content);
    // my_book 首先被 dropped 釋放
    // 緊接著是 my_book.chapter
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容