iT邦幫忙

0

二、三天學一點點 Rust:來! Slices(26)

  • 分享至 

  • xImage
  •  

🧩 Array 與 Slice 的基本概念

Rust 的陣列(Array)與切片(Slice)是安全、靜態類型的資料結構。我們將從三個面向介紹:基本切片操作、切片的型別自動轉換(Deref Coercion),以及可變切片的應用。


📌 第一段:Array Slices(陣列切片基本操作)

這邊將從一個固定大小的陣列中創建「切片」。切片是對陣列一部分(或全部)數據的一個「視圖」或「引用」,它讓我們可以靈活地處理陣列的部分內容,而不需要複製數據。

fn main() {
    let ary = [6, 8, 11, 23, 33, 45];

    let ary_slice = &ary[..4];
    println!("{:?}", ary_slice); // [6, 8, 11, 23]

    let ary_slice = &ary[3..];
    println!("{:?}", ary_slice); // [23, 33, 45]

    let ary_slice = &ary[..];
    println!("{:?}", ary_slice); // [6, 8, 11, 23, 33, 45]

    let ary_slice = &ary;
    println!("{:?}", ary_slice); // 等同於 &ary[..]
}

📖 說明:

  • &ary[..4]:我們使用範圍語法 [..end] 來創建一個切片。這個語法表示從陣列的開頭 (索引 0) 開始,一直到索引 end (但不包含 end 本身)。因此,[..4] 會選取索引 0, 1, 2, 3 的元素。結果如程式碼所示。
  • &ary[3..]:語法 [start..] 表示從索引 start 開始,一直到陣列的結尾。[3..] 會選取從索引 3 開始的所有元素。
  • &ary[..] :語法 [..] 是一個方便的寫法,代表選取整個陣列的所有元素來創建一個切片。至於&ary會在下一段程式碼討論,概念其實跟前一篇解引用轉換概念一樣。

切片的型別為 &[i32],是對陣列的一段參照,不擁有所有權。


📌 第二段:Deref Coercion with Array Slices(切片與函式參數)

在上一段程式碼中,我們看到 &ary&ary[..] 的行為非常相似。其背後的原因是因為Rust 的一個強大特性——解引用轉換 (Deref Coercion)。這個特性讓我們的程式碼更具彈性和通用性。

fn main() {
    let ary = [6, 8, 11, 23, 33, 45];

    let ary_slice = &ary[..3];
    print_length(ary_slice);

    let ary_slice = &ary;
    print_length(ary_slice);
}

fn print_length(reference: &[i32]) {
    println!("{:?}", reference.len());
}

📖 說明:

  1. 函式 print_length 的設定:
  • fn print_length(reference: &[i32]) 這個函式表明它接受一個整數切片的引用 &[i32] 作為參數。因為它不關心傳入的數據是來自一個大小為 6 的陣列、大小為 100 的陣列,還是一個 Vec<i32>。只要能從中得到一個 &[i32] 切片,這個函式就能工作。
  1. 情況一:print_length(&ary[..3]);
  • &ary[..3] 創建了一個包含前三個元素的切片 [6, 8, 11]。它的型別是 &[i32]。這個型別與 print_length 函式期望的參數型別 &[i32] 完全匹配。因此,函式會印出這個切片的長度:3。
  1. 情況二:print_length(&ary);
  • 這是解引用轉換發揮作用的地方。&ary 是一個對整個陣列的引用,其型別是 &[i32; 6]。&[i32; 6] (對固定大小陣列的引用) 和 &[i32] (切片引用) 是兩種不同的型別。前者在型別中包含了陣列的大小 6,而後者沒有。然而,Rust 編譯器知道 &[i32; 6] 可以被安全地「看待」成一個 &[i32]。這個自動轉換的過程就是解引用轉換。編譯器會自動將對固定大小陣列的引用轉換為一個涵蓋其所有元素的切片。因為這個方便的轉換,我們可以將 &ary 直接傳遞給期望 &[i32]print_length 函式,而無需手動寫成 print_length(&ary[..])

📌 第三段:Mutable Array Slices(可變切片)

到目前為止,我們看到的切片都是不可變的,意味著我們只能讀取數據,不能修改。然而,Rust 同樣允許我們創建「可變切片」(Mutable Slices),透過它們,我們可以修改原始陣列中的數據。

fn main() {
    let mut new_ary = [5, 10, 15, 20, 25, 30];
    let my_slice_ary = &mut new_ary[1..4];

    println!("{:?}", my_slice_ary); // [10, 15, 20]

    my_slice_ary[0] = 100;
    println!("{:?}", my_slice_ary); // [100, 15, 20]
    println!("{:?}", new_ary);      // [5, 100, 15, 20, 25, 30]
}

📖 說明:

  1. let mut new_ary = ...;
  • 為了能夠修改陣列的內容,或者從中創建一個可變的借用 (borrow),原始的陣列 new_ary 必須被宣告為可變的 (mut)。
  1. let my_slice_ary = &mut new_ary[1..4];
  • 我們使用 &mut 來創建一個可變引用。&mut new_ary[1..4] 創建了一個指向 new_ary 中索引 1, 2, 3 元素的可變切片。my_slice_ary 的型別是 &mut [i32]。它所引用的初始數據是 [10, 15, 20]。輸出結果為: [10, 15, 20]
  1. my_slice_ary[0] = 100;
  • 由於 my_slice_ary 是一個可變切片,我們可以修改它所引用的元素。重要概念: my_slice_ary[0] 指的是這個切片的第一個元素。這個元素對應到的是原始陣列 new_ary 的索引 1。因此,這行程式碼實際上是將 new_ary 陣列在索引 1 的值從 10 修改為 100。在修改後,my_slice_ary 現在引用的數據變成了 [100, 15, 20]
  1. 觀察原始陣列的變化
  • 當我們再次印出 new_ary 時,可以看到透過可變切片所做的修改已經反映到了原始陣列上。修改後的原始陣為 [5, 100, 15, 20, 25, 30]
  1. Rust 的借用規則提醒:
    當一個可變切片(例如 my_slice_ary)存在時,Rust 的借用規則會生效,以確保記憶體安全。在 my_slice_ary 的生命週期內,你不能創建對 new_ary 的任何其他引用(無論是可變的還是不可變的),直到 my_slice_ary 不再被使用為止。這可以防止數據競爭等問題。

✅ 小結

類型 說明
&[T] 陣列切片(只讀)
&mut [T] 可變陣列切片,可修改內容
&[T; N]&[T] 自動轉型(Deref coercion)

透過切片,我們可以有效且安全地存取部分陣列,並根據需求傳遞給函式或進行修改。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言