Vấn đềBạn đang tái cấu trúc một module hoặc chuẩn bị phát hành một phiên bản mới, và trình biên dịch Rust ngăn cản tiến trình của bạn. Điều này thường xảy ra khi bạn cố gắng tham chiếu đến một giá trị được tạo ra tức thì. Trình biên dịch đưa ra một cảnh báo cụ thể:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:10:22
|
10 | let my_ref = &String::from("data");
| ^^^^^^^^^^^^^^^^^^^^ - giá trị tạm thời bị giải phóng ở cuối câu lệnh này
| |
| tạo ra một giá trị tạm thời vốn bị giải phóng khi vẫn đang được sử dụng
11 | println!("{}", my_ref);
| ------ tham chiếu sau đó được sử dụng tại đây
Lỗi này xảy ra vì bạn đang giữ một con trỏ tới thứ gì đó chỉ tồn tại trong thời gian thực thi một dòng mã duy nhất. Rust ngăn chặn điều này để tránh việc bạn tạo ra các "con trỏ treo" (dangling pointers) có thể gây treo ứng dụng sau này.
Tại sao điều này xảy raMọi giá trị trong Rust đều cần một chủ sở hữu rõ ràng. Khi bạn gọi String::from() mà không gán nó cho một biến, Rust sẽ tạo ra một "giá trị tạm thời". Những giá trị tạm thời này thường biến mất ngay khi câu lệnh hiện tại (dấu chấm phẩy) kết thúc.
Việc lấy tham chiếu (&) đến một giá trị tạm thời là rất rủi ro. Khi dấu chấm phẩy đi qua, dữ liệu sẽ bị xóa khỏi bộ nhớ, nhưng tham chiếu của bạn vẫn trỏ vào vùng không gian trống đó. Vì an toàn bộ nhớ là cam kết cốt lõi của Rust, trình biên dịch sẽ không cho phép mã này chạy.
Cách sửa lỗi E0716### 1. Gán giá trị vào một biếnGiải pháp tiêu chuẩn là đặt tên cho giá trị được sở hữu. Việc "ràng buộc biến" này đảm bảo dữ liệu tồn tại cho đến khi kết thúc khối mã hiện tại, thay vì chỉ kết thúc ở dòng đó.
Mã không chính xác:
// String bị xóa ngay sau dòng này
let data_ref = &format!("Error ID: {}", 404);
process_data(data_ref);
Mã đã sửa:
// data_owned giữ cho giá trị tiếp tục tồn tại
let data_owned = format!("Error ID: {}", 404);
let data_ref = &data_owned;
process_data(data_ref);
2. Tách các chuỗi phương thứcCác lập trình viên thường gặp lỗi E0716 khi nối chuỗi nhiều phương thức. Nếu một bước trung gian trả về một đối tượng được sở hữu — như String — nhưng bước tiếp theo lại trả về một tham chiếu đến dữ liệu nội bộ của nó, thì đối tượng ở giữa sẽ bị xóa quá sớm.
Chuỗi phương thức gây lỗi:
let username = get_user_config().name.trim();
// Nếu get_user_config() trả về một struct tạm thời, name.trim()
// sẽ trỏ vào một trường bên trong struct sắp bị xóa.
Cách khắc phục:
let config = get_user_config();
let username = config.name.trim();
// 'config' giờ đây sở hữu dữ liệu cho toàn bộ phạm vi.
3. Sử dụng String Literal cho các hằng sốNếu bạn cần một tham chiếu đến một chuỗi ký tự cố định, hãy tránh việc bọc nó trong một đối tượng String. Sử dụng trực tiếp string literal sẽ cung cấp cho bạn một &'static str được nhúng sẵn vào tệp nhị phân và không bao giờ hết hạn.
// Tránh tạo giá trị tạm thời như thế này:
// let msg = &String::from("system_failure");
// Sử dụng một static string slice thay thế:
let msg: &'static str = "system_failure";
4. Chiến lược 'Leaking'Trong một số trường hợp ngoại lệ hiếm hoi, như khởi tạo toàn cục, bạn có thể cần một tham chiếu tồn tại trong suốt thời gian chạy chương trình. Bạn có thể sử dụng Box::leak để chuyển một giá trị vào heap và nhận lại một tham chiếu 'static. Lưu ý rằng bộ nhớ này sẽ không được thu hồi cho đến khi tiến trình kết thúc.
let long_lived_ref: &'static str = Box::leak(format!("Runtime-{}", id).into_boxed_str());
Các bước kiểm traSau khi áp dụng những thay đổi này, hãy kiểm tra mã của bạn để đảm bảo không có vấn đề mới phát sinh:
- Chạy
cargo check: Lệnh này phân tích mã của bạn mà không tốn tài nguyên biên dịch đầy đủ. Nó thường nhanh hơn từ 2 đến 5 lần so với build hoàn chỉnh và phát hiện lỗi borrow checker ngay lập tức.- Phân tích phạm vi (Scope): Đảm bảo biến chủ sở hữu được định nghĩa trong cùng một khối mã với tham chiếu. Nếu bạn đưa tham chiếu ra ngoài khối đó, lỗi sẽ quay trở lại.- Chạy Unit Test: Thực thicargo testđể xác nhận logic của bạn vẫn ổn định. Rust ngăn chặn lỗi bộ nhớ, nhưng nó không ngăn được các lỗi logic do thay đổi quyền sở hữu dữ liệu.## Mẹo tóm tắt- Xác định chủ sở hữu: Khi lỗi E0716 xuất hiện, hãy xác định biến nào sở hữu dữ liệu. Nếu không có biến nào sở hữu, hãy tạo ra một biến.- Tôn trọng trình biên dịch: Rust đang ngăn chặn một lỗi segmentation fault tiềm ẩn. Đừng dùngunsafeđể bỏ qua các kiểm tra này trừ khi bạn đang xây dựng các thành phần cấp thấp.- Chú ý các phương thức chuỗi: Các phương thức như.as_str()hoặc.as_bytes()là những tác nhân phổ biến. Chúng tạo ra tham chiếu đến dữ liệu mà dữ liệu đó phải được sở hữu bởi một biến cục bộ để duy trì tính hợp lệ.

