blog.dopana

Back

Bài thứ sáu trong loạt Rust cơ bản. Ownership đảm bảo an toàn bộ nhớ, nhưng di chuyển giá trị khắp nơi nhanh chóng trở nên bất tiện. Borrowing (mượn) giúp bạn truy cập dữ liệu mà không cần nhận quyền sở hữu.

Mượn dữ liệu bằng tham chiếu#

Tham chiếu cho phép bạn tham chiếu đến một giá trị mà không lấy quyền sở hữu của nó. Tham chiếu được tạo bằng toán tử &:

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

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("Chiều dài của '{s1}' là {len}.");
}
rust

calculate_length nhận tham chiếu &String, s1 được mượn chứ không bị di chuyển. s1 vẫn hợp lệ sau lời gọi hàm.

Tham chiếu khả biến#

Mặc định, các tham chiếu là bất biến. Để chỉnh sửa dữ liệu được mượn, dùng tham chiếu khả biến &mut:

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

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

Để tạo tham chiếu khả biến, biến gốc cũng phải được khai báo với mut.

Quy tắc cốt lõi của borrowing#

Rust thực thi quy tắc nghiêm ngặt để ngăn chặn xung đột dữ liệu (data race) ngay từ lúc biên dịch:

Tại một thời điểm, bạn chỉ có thể có nhiều tham chiếu bất biến (&T) hoặc duy nhất một tham chiếu khả biến (&mut T), không thể có cả hai.

let mut s = String::from("hello");

let r1 = &s; // OK
let r2 = &s; // OK
// let r3 = &mut s; // LỖI: không thể mượn `s` dưới dạng khả biến vì nó đang được mượn bất biến
println!("{r1} và {r2}");
rust

Quy tắc “aliasing XOR mutability” này đảm bảo dữ liệu không bị thay đổi bất ngờ khi phần khác của chương trình đang đọc.

Slice#

Slice là tham chiếu đến một chuỗi các phần tử liên tiếp trong một collection, thay vì toàn bộ collection.

String slice#

String slice (&str) tham chiếu đến một phần của String:

let s = String::from("hello world");

let hello: &str = &s[0..5];
let world: &str = &s[6..11];
rust

Chuỗi literal ("hello") thực chất là một string slice trỏ trực tiếp vào dữ liệu trong file nhị phân đã biên dịch.

Slice trên mảng#

Slice cũng hoạt động trên mảng và vector:

let a = [1, 2, 3, 4, 5];
let slice: &[i32] = &a[1..3];

assert_eq!(slice, &[2, 3]);
rust

Kết luận#

Mượn dữ liệu bằng tham chiếu (&&mut) giúp chia sẻ dữ liệu an toàn. Slice (&str&[T]) cung cấp góc nhìn linh hoạt, chỉ đọc trên các chuỗi phần tử. Bài tiếp theo trong loạt: struct.

Tài liệu tham khảo#