Vấn đề
Có thể bạn vừa chuyển mã nguồn sang một module riêng để cấu trúc dự án gọn gàng hơn, nhưng lại thấy nó không còn biên dịch được nữa. Rust tiếp cận dữ liệu theo nguyên tắc "chỉ biết khi cần". Theo mặc định, mọi trường (field) trong struct đều là riêng tư (private), ngay cả khi chính struct đó được đánh dấu là công khai (public).
Lỗi này thường xuất hiện trong quá trình tái cấu trúc mã nguồn (refactoring). Bạn có thể chuyển từ một tệp main.rs duy nhất dài 300 dòng sang một dự án đa tệp và nhận ra các module con không còn có thể "nhìn thấy" dữ liệu nội bộ của nhau. Nếu một trường không được đánh dấu rõ ràng để xuất ra ngoài, trình biên dịch sẽ chặn quyền truy cập để bảo vệ tính đóng gói.
Thông báo lỗi
Trình biên dịch rất cụ thể khi điều này xảy ra. Bạn sẽ thấy một kết quả tương tự như sau trong terminal của mình:
error[E0616]: field `balance` of struct `Account` is private
--> src/main.rs:10:20
|
10 | println!("{}", account.balance);
| ^^^^^^^ trường riêng tư
Tại sao Rust lại khắt khe ở đây
Nếu bạn đến từ Java hoặc C#, bạn có thể mong đợi một class public sẽ tự động để lộ các thành viên của nó. Rust yêu cầu sự cho phép rõ ràng cho từng trường một. Ngay cả khi bạn định nghĩa pub struct User, các trường bên trong vẫn bị ẩn đối với bất kỳ mã nguồn nào bên ngoài module cụ thể đó. Sự khắt khe này giúp ngăn chặn tình trạng "spaghetti code", nơi mọi phần của ứng dụng đều can thiệp vào trạng thái nội bộ của các phần khác.
Các bước khắc phục
Cách 1: Khắc phục nhanh (pub)
Nếu một trường thực sự được thiết kế để trở thành một phần trong API công khai của bạn, hãy thêm từ khóa pub. Đây là cách tiếp cận tiêu chuẩn cho các container dữ liệu đơn giản, như struct Point hoặc Config, nơi không có quy tắc nội bộ phức tạp nào cần bảo vệ.
Trước:
pub struct User {
username: String,
}
Sau:
pub struct User {
pub username: String, // Bây giờ có thể truy cập từ bên ngoài module
}
Cách 2: Giải pháp trung gian (pub(crate))
Nếu bạn muốn toàn bộ dự án của mình thấy một trường, nhưng muốn ẩn nó đối với những người dùng bên ngoài thư viện của bạn thì sao? Hãy sử dụng pub(crate). Nó cho phép hiển thị trong phạm vi dự án (crate) của bạn trong khi vẫn giữ cho API bên ngoài sạch sẽ và an toàn.
pub struct Database {
pub(crate) connection_string: String, // Chỉ hiển thị trong dự án của bạn
password: String, // Hoàn toàn riêng tư trong module này
}
Bạn cũng có thể sử dụng pub(super) để giới hạn khả năng hiển thị chỉ trong module cha.
Cách 3: Lựa chọn chuyên nghiệp (Getter Methods)
Việc để lộ trực tiếp các trường thường là một thói quen rủi ro. Nó ngăn cản bạn thay đổi kiểu dữ liệu nội bộ sau này mà không làm hỏng mọi đoạn mã đang sử dụng nó. Thay vào đó, hãy giữ các trường ở chế độ riêng tư và cung cấp một phương thức công khai để đọc dữ liệu.
Trong src/models.rs:
pub struct Account {
balance: f64,
}
impl Account {
pub fn new(amount: f64) -> Self {
Self { balance: amount }
}
// Getter chỉ đọc
pub fn balance(&self) -> f64 {
self.balance
}
}
Trong src/main.rs:
mod models;
use models::Account;
fn main() {
let my_acc = Account::new(1500.50);
// println!("{}", my_acc.balance); // Dòng này sẽ gây lỗi E0616
println!("Số dư hiện tại: {}", my_acc.balance()); // Dòng này hoạt động!
}
Xác minh
Chạy cargo check trong terminal để xác minh việc sửa lỗi. Nó nhanh hơn đáng kể so với việc build đầy đủ vì nó bỏ qua giai đoạn tạo mã. Nếu lỗi E0616 biến mất, cài đặt hiển thị của bạn đã chính xác. Nếu bạn chọn phương pháp Getter, hãy đảm bảo bạn đã cập nhật mã gọi hàm để sử dụng cú pháp account.balance() thay vì account.balance.
Mẹo chuyên nghiệp về khả năng hiển thị
- **Quy tắc 80/20:** Cố gắng giữ 80% các trường struct ở chế độ riêng tư. Chỉ để lộ những gì thực sự cần thiết để phần còn lại của chương trình hoạt động.
- **Truy cập của module con:** Trong Rust, các module con có thể thấy các trường riêng tư của module cha, nhưng các module cha không thể nhìn thấy các trường riêng tư của module con.
- **Logic xác thực:** Sử dụng các trường riêng tư kết hợp với các phương thức "setter" nếu bạn cần đảm bảo một giá trị—chẳng hạn như `user_age`—không bao giờ xuống dưới 0.
- **Quyền riêng tư cấp Crate:** Sử dụng `pub(crate)` rộng rãi trong các dự án lớn để tránh rò rỉ các chi tiết triển khai nội bộ cho người dùng cuối.

