iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0
Software Development

Rust 學得動嗎系列 第 4

[Day 4] Rust 的核心:深入理解所有權系統

  • 分享至 

  • xImage
  •  

今天來看 Rust 最獨特和最重要的特性之一:所有權系統-ownership。這個系統是 Rust 實現記憶體安全和並行安全的基礎,也是區別於其他語言的關鍵特性。

所有權規則

Rust 的所有權系統遵循三個基本規則:

  1. Rust 中的每一個值都有一個被稱為其所有者(owner)的變數。
  2. 值在任一時刻有且只有一個所有者。
  3. 當所有者(變數)離開作用域,這個值將被丟棄。

變數作用域

在 Rust 中,變數的作用域是一個重要概念:

{                      // s 在這裡無效,它尚未聲明
    let s = "hello";   // 從這裡開始,s 是有效的
    // 使用 s
}                      // 此作用域已結束,s 不再有效

Move

Rust 使用移動(move)語義來管理heap上的資料:

let s1 = String::from("hello");
let s2 = s1;  // s1 被移動到 s2,s1 不再有效

// println!("{}", s1);  // 這行會導致編譯錯誤

Clone

如果我們確實需要深度複製heap上的資料,可以使用 clone 方法:

let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);  // 這行正常工作

Copy

對於存儲在stack上的簡單類型,Rust 使用複製語義:

let x = 5;
let y = x;  // x 被複製到 y,兩者都有效
println!("x = {}, y = {}", x, y);  // 正常工作

所有權與函數

函數參數和返回值也涉及所有權轉移:

fn main() {
    let s = String::from("hello");
    takes_ownership(s);  // s 的值移動到函數裡
    // println!("{}", s);  // 這行會報錯,s 已經無效

    let x = 5;
    makes_copy(x);  // x 的值被複製到函數裡
    println!("{}", x);  // 這行正常工作
}

fn takes_ownership(some_string: String) {
    println!("{}", some_string);
}

fn makes_copy(some_integer: i32) {
    println!("{}", some_integer);
}

返回值與作用域

函數返回值也可以轉移所有權:

fn main() {
    let s1 = gives_ownership();
    let s2 = String::from("hello");
    let s3 = takes_and_gives_back(s2);
}

fn gives_ownership() -> String {
    let some_string = String::from("yours");
    some_string
}

fn takes_and_gives_back(a_string: String) -> String {
    a_string
}

引用與借用

為了避免所有權轉移帶來的不便,Rust 提供了引用(reference)機制:

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

這裡 &s1 創建了一個指向 s1 的引用,但並不擁有它。這個過程被稱為借用(borrowing)。

可變引用

如果我們想要修改借用的值,可以使用可變引用:

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

注意:在特定作用域中,對於某個特定數據,要麼只能有一個可變引用,要麼可以有任意多個不可變引用。

懸垂引用(Dangling References)

Rust 編譯器確保引用永遠不會變成懸垂引用:

fn main() {
    let reference_to_nothing = dangle();
}

fn dangle() -> &String {  // 錯誤:返回的引用指向一個無效數據
    let s = String::from("hello");
    &s
}  // s 在這裡離開作用域並被丟棄

切片類型

切片(Slice)是對集合中部分連續元素的引用:

let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];

字符串字面值就是切片:

let s: &str = "Hello, world!";

明天,我們再來討論Rust生命週期概念,這是與所有權系統緊密相關的另一個重要 Rust 特性。


上一篇
[Day 3] Rust實戰:建立專案、RustRover開發與GitHub上傳
下一篇
[Day 5] Rust 生命週期:確保引用有效性的關鍵
系列文
Rust 學得動嗎30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言