References trong Rust

0 0 0

Người đăng: Le Thanh Cong

Theo Viblo Asia

Trong Rust, con trỏ có thể được phân loại theo cách quản lý ownership. Dựa vào yếu tố đó ta có thể chia làm 2 loại:

  • Owning Pointers: Là loại con trỏ mà khi bị hủy, dữ liệu mà nó trỏ đến cũng sẽ được giải phóng. Tiêu biểu như String, Box<T>, Rc, Arc, Vec ...
  • Non-Owning Pointers: Là loại con trỏ không sở hữu dữ liệu trỏ đến. Chúng chỉ là các tham chiếu và không chịu trách nhiệm về việc quản lý vòng đời của dữ liệu đó.

Rust gọi các con trỏ Non-Owning Pointers với cái tên ngắn gọn hơn là References, chủ đề mà chúng ta sẽ cùng tìm hiểu ngày hôm nay.

Tham chiếu tới giá trị (References to Values)

use std::collections::HashMap;
type Table = HashMap<String, Vec<String>>; fn show(table: Table) { for (artist, works) in table { println!("works by {}:", artist); for work in works { println!(" {}", work); } }
} fn main() { let mut table = Table::new(); table.insert( "Gesualdo".to_string(), vec![ "many madrigals".to_string(), "Tenebrae Responsoria".to_string(), ], ); table.insert( "Caravaggio".to_string(), vec![ "The Musicians".to_string(), "The Calling of St. Matthew".to_string(), ], ); table.insert( "Cellini".to_string(), vec![ "Perseus with the head of Medusa".to_string(), "a salt cellar".to_string(), ], ); show(table); // chuyển quyền sở hữu table cho hàm show assert_eq!(table["Gesualdo"][0], "many madrigals"); // Báo lỗi: borrow of moved value: `table`
}

Hàm show chỉ có nhiệm vụ khá đơn giản in ra màn hình, với cách gọi như trên thì chúng ta sẽ không thể tương tác tiếp với table sau khi gọi hàm show => vô cùng bất tiện 😤

Chúng ta sẽ sử dụng tham chiếu (references) để giải quyết vấn đề trên.

fn show(table: &Table) { // thay đổi kiểu dữ liệu của truyền vào thành dạng tham chiếu for (artist, works) in table { println!("works by {}:", artist); for work in works { println!(" {}", work); } }
} fn main() { // ... show(&table); // Truyền tham chiếu assert_eq!(table["Gesualdo"][0], "many madrigals");
}

Tham chiếu lại được chia thành 2 kiểu:

  • Shared Reference (&T): Là một tham chiếu bất biến (immutable) đến dữ liệu. Nó cho phép bạn đọc dữ liệu nhưng không thể thay đổi nó.
  • Mutable Reference (&mut T): Là một tham chiếu có thể thay đổi dữ liệu. Chúng ta có quyền thay đổi giá trị mà nó trỏ đến.
// ví dụ cho Mutable Reference
fn sort_works(table: &mut Table) { for (_artist, works) in table { works.sort(); }
} sort_works(&mut table);

Ví dụ khi sử dụng tham chiếu

Tham chiếu Rust so với tham chiếu của C/C++

Trong C++, tham chiếu được tạo một cách ngầm định thông qua ép kiểu, và việc giải tham chiếu (dereferenced) cũng được thực hiện một cách ngầm định.

Trong Rust, tham chiếu được tạo ra một cách tường minh bằng toán tử &, và việc dereferenced được sử dụng một cách tường minh bằng toán tử *:

// C++
int x = 10;
int &r = x; // Ép kiểu x thành dạng tham chiếu
assert(r == 10); // ngầm định lấy ra giá trị mà tham chiếu trỏ đến
r = 20; // thay giá trị của x thông qua r
// Rust
let x = 10;
let r = &x; // Khởi tạo biến r là 1 tham chiếu đến x assert!(*r == 10); // Lấy giá trị mà r tham chiếu trỏ tới bằng ký tự *

Nếu muốn sửa giá trị qua tham chiếu thì phải thêm mut, chặt chẽ hơn nhiều so với C/C++

let mut y = 32;
let m = &mut y; // biến m là tham chiếu dạng mut tới y
*m += 32; // tăng giá trị của y thêm 32 đơn vị
assert!(*m == 64);

Tuy nhiên, với toán tử . thì Rust sẽ ngầm định lấy ra giá trị tham chiếu tới. Cùng xem qua các ví dụ dưới đây

fn main() { struct Anime { name: &'static str, bechdel_pass: bool, }; let aria = Anime { name: "Aria: The Animation", bechdel_pass: true, }; let anime_ref = &aria; // lấy giá trị name ra với tham chiếu anime_ref assert_eq!((*anime_ref).name, "Aria: The Animation"); // Cũng là lấy ra giá trị name, nhưng ngắn gọn hơn assert_eq!(anime_ref.name, "Aria: The Animation");
}
let mut v = vec![1973, 1968];
(&mut v).sort(); // tương đương cách trên nhưng ngắn gọn hơn
v.sort();

Tham chiếu trỏ tới tham chiếu

struct Point { x: i32, y: i32 } let point = Point { x: 1000, y: 729 }; let r: &Point = &point;
let rr: &&Point = &r;
let rrr: &&&Point = &rr; // Mặc dù trỏ qua nhiều tham chiếu trung gian
// nhưng khi dùng toán tử . thì Rust sẽ hỗ trợ truy tới giá trị đích luôn
assert_eq!(rrr.y, 729);

So sánh tham chiếu

fn main() { let x = 10; let y = 10; let rx = &x; let ry = &y; let rrx = &rx; let rry = &ry; // So sánh giá trị mà tham chiếu trỏ tới println!("{}", rrx <= rry); // true (10 <= 10) println!("{}", rrx == rry); // true (10 == 10) println!("{}", rx == ry); // true (10 == 10) // so sánh 2 con trỏ có cùng trỏ đến 1 vùng nhớ không ? println!("{}", std::ptr::eq(rx, ry)); // false
}

Con Trỏ Null ?

Trong Rust, khái niệm về con trỏ NULL không tồn tại. Thay vào đó, ngôn ngữ sử dụng kiểu dữ liệu Option<T> để xử lý các trường hợp mà một giá trị có thể không tồn tại. Điều này giúp loại bỏ hoàn toàn các vấn đề liên quan đến con trỏ NULL, như lỗi truy cập bộ nhớ không xác định hoặc con trỏ lơ lửng (dangling pointers) rất hay gặp trong các ngôn ngữ lập trình khác như C/C++.

Bằng cách không cho phép giá trị NULL, Rust buộc lập trình viên phải xử lý tất cả các trường hợp có thể xảy ra, từ đó cải thiện tính ổn định và độ tin cậy của ứng dụng.

fn find_value(list: &Vec<i32>, target: i32) -> Option<&i32> { for &item in list { if item == target { return Some(&item); // Trả về giá trị nếu tìm thấy } } None // Không tìm thấy
} fn main() { let numbers = vec![10, 20, 30, 40]; let target_value = 25; match find_value(&numbers, target_value) { Some(result) => println!("Found value: {}", result), None => println!("Value not found."), }
}

Tài liệu tham khảo

https://www.oreilly.com/library/view/programming-rust-2nd/9781492052586/

Bình luận

Bài viết tương tự

- vừa được xem lúc

Chuyện cái comment

Chuyện cái comment. Chuyện rằng, có một ông bạn nọ có cái blog ở trên mạng, cũng có dăm.

0 0 36

- vừa được xem lúc

Đừng đánh nhau với borrow checker

Đừng đánh nhau với borrow checker. TL;DR: Đừng bao giờ đánh nhau với borrow checker, nó được sinh ra để bạn phải phục tùng nó .

0 0 31

- vừa được xem lúc

Chuyện biểu diễn ma trận trên máy tính

Chuyện biểu diễn ma trận trên máy tính. Cách đây mấy hôm mình có share cái screenshot trên Facebook, khoe linh tinh vụ mình đang viết lại cái CHIP-8 emulator bằng Rust.

0 0 45

- vừa được xem lúc

Rust và Lập trình Web

Rust và Lập trình Web. Bài viết được dịch lại từ bản gốc "Rust for the Web" đăng tại phiên bản blog tiếng Anh của mình.

0 0 40

- vừa được xem lúc

Viết ứng dụng đọc tin HackerNews bằng Rust

Viết ứng dụng đọc tin HackerNews bằng Rust. Dạo này mình toàn viết bài linh tinh, lâu rồi chưa thấy viết bài kĩ thuật nào mới nên hôm nay mình viết trở lại, mất công các bạn lại bảo mình không biết co

0 0 28

- vừa được xem lúc

Cài đặt Rust trên Arch Linux

Cài đặt Rust trên Arch Linux. Việc cài đặt Rust trên môi trường Arch Linux khá là đơn giản.

0 0 46