切片 &[T]
本集目標
用切片(slice)取出陣列的一部分,像透過窗戶看裡面的東西。
正文
有時候你不需要整個陣列,只想看其中一段。比如一個有 5 個元素的陣列,你只想看第 2 到第 4 個。這時候就可以用切片(slice)。
基本語法
fn main() {
let arr = [1, 2, 3, 4, 5];
let slice = &arr[1..4];
println!("{:?}", slice);
}
和陣列、tuple 一樣,切片也只能用 {:?} 來印,不能用 {}。
&arr[1..4] 的意思是:「從索引 1 開始,到索引 4 之前為止。」
- 索引 1 → 2(包含)
- 索引 2 → 3(包含)
- 索引 3 → 4(包含)
- 索引 4 → 5(不包含)
所以結果是 [2, 3, 4]。
範圍的寫法
fn main() {
let arr = [1, 2, 3, 4, 5];
let a = &arr[0..3]; // [1, 2, 3] 從 0 到 3(不含 3)
let b = &arr[0..=2]; // [1, 2, 3] 從 0 到 2(包含 2)
let c = &arr[2..]; // [3, 4, 5] 從 2 到最後
let d = &arr[..3]; // [1, 2, 3] 從頭到 3(不含 3)
let e = &arr[..]; // [1, 2, 3, 4, 5] 整個陣列
println!("{:?}", a);
println!("{:?}", b);
println!("{:?}", c);
println!("{:?}", d);
println!("{:?}", e);
}
1..4→ 從 1 到 4(不含 4)1..=3→ 從 1 到 3(包含 3),還記得第一章第 20 集的..=嗎?一樣的用法2..→ 從 2 到結尾..3→ 從開頭到 3(不含 3)..→ 整個
切片是「視窗」,不是「複製」
這裡有個重要的觀念:切片不是把資料複製一份出來,而是「指向原本陣列的某一段」。就像透過窗戶看房間裡的東西——東西還是在房間裡,你只是從窗戶看進去。
fn main() {
let arr = [10, 20, 30, 40, 50];
let slice = &arr[1..4];
println!("陣列:{:?}", arr);
println!("切片:{:?}", slice);
}
那個 & 是什麼?
你可能注意到切片前面有個 &。這個符號代表「借用」(borrow),是 Rust 最重要的概念之一。但現在不用深入理解——先記住「切片要加 &」就好,之後我們會詳細解釋。
現在你只需要知道:寫切片的時候前面要加 &。
切片的型別
還記得陣列的型別是 [i32; 5](型別包含長度)嗎?切片的型別是 &[i32]——沒有長度:
fn main() {
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let slice: &[i32] = &arr[1..4];
println!("{:?}", slice);
}
&[i32] 代表「一段 i32 的切片」,不管長度是多少。這是切片和陣列最大的不同——陣列的長度是型別的一部分([i32; 3] 和 [i32; 5] 是不同型別),但切片不管長度,&[i32] 可以指向任意長度的連續區段。
走訪切片
切片也可以用 for 走訪:
fn main() {
let arr = [1, 2, 3, 4, 5];
let slice = &arr[1..4];
for x in slice {
println!("{}", x);
}
}
複合型別
學完切片之後,讓我們整理一下:到目前為止,我們學過兩類型別。
基本型別(primitive types): i32、f64、bool、char 等等,每個值就是一個單獨的東西。
複合型別(compound types): 把其他型別組合在一起的型別。我們已經學了三種:
- tuple:
(i32, f64, bool)— 可以裝不同型別 - 陣列:
[i32; 5]— 同一種型別,固定長度 - 切片:
&[i32]— 同一種型別,不限長度
複合型別裡面的型別不一定要是基本型別,也能是一層複合型別套另一層複合型別:
fn main() {
// 陣列裡面裝 tuple
let pairs: [(i32, bool); 3] = [(1, true), (2, false), (3, true)];
println!("{:?}", pairs);
// tuple 裡面裝陣列
let t: ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]);
println!("{:?}", t);
// 陣列裡面裝陣列
let grid: [[i32; 2]; 3] = [[1, 2], [3, 4], [5, 6]];
println!("{:?}", grid);
// 切片也是複合型別
let arr: [i32; 5] = [10, 20, 30, 40, 50];
let slice: &[i32] = &arr[1..4];
println!("{:?}", slice);
// tuple 裡面裝切片
let pair: (&[i32], &[i32]) = (&arr[..2], &arr[3..]);
println!("{:?}", pair);
}
重點整理
- 切片用
&arr[start..end]或&arr[start..=end]取出陣列的一部分 start..end是「包含 start、不包含 end」start..=end是「包含 start 也包含 end」- 切片是陣列的「視窗」,不是複製
- 切片型別是
&[i32](不含長度),陣列型別是[i32; 5](含長度) - 前面的
&代表借用,之後會詳細解釋 - 切片也可以用
for走訪 - tuple、陣列、切片都是複合型別——它們裡面可以裝其他型別,包含複合型別