ข้ามไปยังเนื้อหา

Slices

slice คือ reference ไปยังลำดับของ element ที่ต่อเนื่องกันใน collection ไม่ได้เป็นเจ้าของข้อมูล — เป็นการ borrow view เข้าไปยังหน่วยความจำที่มีอยู่แล้ว

slice มีสอง type ที่พบบ่อย:

  • &[T] — slice ของ array หรือ vector ใด ๆ ของ type T
  • &str — string slice; view เข้าไปยังข้อมูล string แบบ UTF-8

เพราะ slice เป็น reference เสมอ lifetime ของ slice จึงผูกอยู่กับ collection ที่ borrow มา

เมื่อมี array หรือ vector คุณสร้าง slice ได้โดยใช้ range syntax ภายในวงเล็บเหลี่ยม:

fn main() {
let numbers = [10, 20, 30, 40, 50];
let all: &[i32] = &numbers[..]; // entire array
let first_three: &[i32] = &numbers[..3]; // indices 0, 1, 2
let middle: &[i32] = &numbers[1..4]; // indices 1, 2, 3
let last_two: &[i32] = &numbers[3..];// indices 3, 4
println!("all: {:?}", all);
println!("first three: {:?}", first_three);
println!("middle: {:?}", middle);
println!("last two: {:?}", last_two);
}

สรุป range syntax:

Syntaxความหมาย
[..]ทั้ง collection
[..n]n element แรก (index 0 ถึง n-1)
[m..n]index m ถึง n-1
[m..]จาก index m ไปจนสุดท้าย

string slice (&str) คือ reference ไปยังบางส่วนของ String หรือ string literal ตัว string literal เองก็เป็น &str — เป็น slice ของข้อมูลแบบ read-only ที่ฝังอยู่ใน binary

fn main() {
let s = String::from("hello world");
let hello: &str = &s[..5]; // "hello"
let world: &str = &s[6..]; // "world"
println!("{} {}", hello, world);
// A string literal is already &str
let literal: &str = "Rust is fast";
println!("{}", literal);
}

หากไม่มี slice คุณอาจเก็บ index เข้าไปใน string แล้วแก้ไข string นั้น — ทำให้ index ชี้ไปยังข้อมูลที่ไม่มีความหมาย ด้วย string slice borrow checker จะรับประกันว่า slice ไม่สามารถมีอายุยืนกว่าข้อมูลที่อ้างถึงได้

fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &byte) in bytes.iter().enumerate() {
if byte == b' ' {
return &s[..i]; // slice up to the space
}
}
s // whole string is one word
}
fn main() {
let sentence = String::from("hello world");
let word = first_word(&sentence);
// sentence cannot be mutated while word (a slice of it) is alive
println!("first word: {}", word);
}

ฟังก์ชันที่ทำงานกับลำดับข้อมูลควรรับ slice แทนที่จะเป็น type ที่เป็นเจ้าของค่า วิธีนี้ทำให้ฟังก์ชันทำงานได้กับ array, vector และบางส่วนของทั้งสองอย่างโดยไม่ต้องขอ ownership

  • ควรเลือกใช้ &[T] แทน &Vec<T> สำหรับพารามิเตอร์ที่เป็นลำดับข้อมูล
  • ควรเลือกใช้ &str แทน &String สำหรับพารามิเตอร์ที่เป็น string

ทั้ง &Vec<T> และ &String จะ coerce ไปเป็น slice ที่เทียบเท่ากันโดยอัตโนมัติ ณ จุดที่เรียก

fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &byte) in bytes.iter().enumerate() {
if byte == b' ' {
return &s[..i];
}
}
s
}
fn sum_slice(nums: &[i32]) -> i32 {
let mut total = 0;
for &n in nums {
total += n;
}
total
}
fn main() {
// String slice
let sentence = String::from("hello world");
let word = first_word(&sentence);
println!("first word: {}", word);
// Integer slice
let numbers = [10, 20, 30, 40, 50];
let middle: &[i32] = &numbers[1..4];
println!("middle slice: {:?}", middle);
println!("sum of middle: {}", sum_slice(middle));
// String slice literal
let greeting: &str = "Rust is safe";
println!("slice: {}", &greeting[..4]);
}
type ใดแทน view ที่ borrow มาเข้าไปยังบางส่วนของ String?
range syntax [1..4] เลือกอะไรออกมาจาก slice?
slice เป็นเจ้าของข้อมูลที่ชี้ไปหรือไม่?
ทำไมพารามิเตอร์ของฟังก์ชันจึงควรใช้ &[T] แทน &Vec<T>?