จากบทความที่แล้ว Rust language: Data Types เราได้มีการพูดถึงตัวแปรแบบ array ที่มีข้อจำกัดในเรื่องของขนาดของ array ที่ถูก fixed size ไว้ ไม่สามารถเพิ่มหรือลดขนาดได้

หากเราต้องการเก็บข้อมูลแบบกลุ่มโดยที่ให้ตัวแปรนั้นสามารถขยายหรือลดขนาดได้ ให้เลือกใช้งาน vector แทนการใช้ array

ก่อนอื่น ต้องทำความเข้าใจกันก่อนว่า Vectors, String และ hash map นั้น ไม่ได้เป็น Data Type พื้นฐาน เหมือนบทความที่แล้ว

แต่มันจะอยู่ในกลุ่มของ standard library ที่ไว้สำหรับจัดการกับโครงสร้างข้อมูล (data structures) หรือเรียกอีกอย่างว่า Collections Data type อื่นๆ ส่วนใหญ่มีไว้เพื่อเก็บค่าแค่ค่าเดียว แต่ collection สามารถเก็บค่าไว้หลายค่าได้

โดย data collections เหล่านี้จะชี้ไปที่ๆ เก็บมันเอาใน heap (memory) ให้นึกถึง pointer ของภาษา c หรือ go ซึ่งมันทำให้เราสามารถเก็บและเข้าถึงข้อมูลได้โดยไม่จำเป็นต้องรู้จำนวนของข้อมูล


Vector

เริ่มต้นที่ หากเราต้องการสร้าง vector ใหม่ ที่ไม่มีการกำหนดค่าเริ่มต้นอะไรไว้ เราสามารถเรียกใช้งานผ่านคำสั่ง Vec::new

fn main() {
    let v: Vec<i32> = Vec::new();

    println!("The value of v is: {:?}", v);
}

new vector

หากต้องการกำหนดค่าเริ่มต้นด้วย

fn main() {
    let v = vec![1, 2, 3];
    
    println!("The value of v is: {:?}", v);
}

new vector with default

Updating a Vector

เมื่อเราต้องการที่จะเพิ่มข้อมูลเข้าไปยัง vector ที่สร้างไว้ เราจะใช้คำสั่ง push อย่าลืมเพิ่ม mut เพื่อบอก compiler ให้รู้ว่าตัวแปรนี้สามารถแก้ไขได้

fn main() {
    let mut v: Vec<i32> = Vec::new();

    println!("The value of v is: {:?}", v);

    v1.push(5);
    v1.push(6);
    v1.push(7);
    v1.push(8);

    println!("The value of v is: {:?}", v);
}

update data in vector

จากโค๊ด สังเกตว่า ถ้าหากเราต้องการจะ print ตัวแปร vector ออกมาจะต้องใส่ {:?} ไว้ด้วยเสมอ ตัวนี้เป็นเหมือนกับการ debug mode เพื่อเข้าถึงค่าของตัวแปลนั้น

Reading Elements of Vectors

การอ่านข้อมูลใน vector มี 2 วิธี ในการอ้างอิงค่าที่เก็บไว้ใน vector ผ่าน index หรือใช้ฟังก์ชั่น get

fn main() {
    let v = vec![1, 2, 3, 4, 5];

    let third: &i32 = &v[2];
    println!("The third element is {third}");

    let third: Option<&i32> = v.get(2);
    match third {
        Some(third) => println!("The third element is {third}"),
        None => println!("There is no third element."),
    }
}

reading elements of vector

จากโค๊ด การเข้าถึงข้อมูลใน vector แบบ index ก็เหมือนกับภาษาอื่นๆ ที่ใช้ [] แต่ที่น่าสนใจเลย คือ การเข้าถึงข้อมูลใน vector แบบฟังก์ชั่น get เมื่อเราใช้เมธอด get โดยที่ส่ง index ผ่านเป็น argument เราจะได้รับ Option<&T> ที่เราสามารถใช้ในการกับ match ได้


Slice

Slice เป็น ประเภทข้อมูลที่ใช้ในการอ้างอิงถึง collection ที่เก็บข้อมูลไว้ โดย slice นั้นจะไม่สามารถแก้ไข element ใน slice ได้โดยตรง แต่มันจะใช้หน่วยความจำน้อยกว่าการเข้าถึง array หรือ vector โดยตรง ทำให้การเข้าถึงข้อมูลผ่าน slice จะทำงานได้เร็วกว่าการเข้าถึงโดยตรง

เนื่องจาก slice เป็นการ reference ข้อมูล เลยทำให้ตัวของมันจะไม่มีความเป็น ownership (ไว้ค่อยอธิบายเรื่อง ownership ในบทความต่อไป)

การสร้าง Slice:

  • จาก array หรือ vector: ใช้ & และ index range
  • จาก string literal: ใช้ &str
  • ถ้าต้องการเก็บค่าจาก slice อื่น: ให้ใช้ clone()
fn main() {
    let my_array: [i32; 5] = [1, 2, 3, 4, 5];
    let my_slice: &[i32] = &my_array[1..4];
    
    println!("The slice is: {:?}", my_slice);
}

slice with index range

เราสามารถใช้ slice กับ string ได้ แต่จะไม่สามารถแก้ไขมันได้

fn main() {
    let my_str: &str = "Hello, world!";
    println!("The my_str is: {:?}", my_str);
}

slice with string literal

ถ้าหากต้องการเก็บค่าจาก slice อื่น ให้ใช้ clone()

fn main() {
    let my_array: [i32; 5] = [1, 2, 3, 4, 5];
    let my_slice: &[i32] = &my_array[1..4];
    
    let my_slice2: &[i32] = my_slice.clone();
    println!("The my_slice2 is: {:?}", my_slice2);
}

copy data from slice


ทั้งหมดที่อ่านมาเป็นเพียงแค่วิธีการใช้ง slice เบื้องต้น ซึ่งการใช้งานจริงๆ จำเป็นต้องทำความเข้าใจ และเลือกใช้งานให้เหมาะสม เพื่อช่วยในการบริหารจัดการ memory ของระบบที่เราเขียนมันขึ้นมา