Dễ dàng nhất để hiểu các thời gian sống không từ vựng là gì bằng cách hiểu các thời gian sống từ vựng là gì. Trong các phiên bản của Rust trước khi có các vòng đời không phải từ vựng, mã này sẽ không thành công:
fn main() {
let mut scores = vec![1, 2, 3];
let score = &scores[0];
scores.push(4);
}
Trình biên dịch Rust thấy rằng biến đó scores
được mượn score
, vì vậy nó không cho phép đột biến thêm về scores
:
error[E0502]: cannot borrow `scores` as mutable because it is also borrowed as immutable
--> src/main.rs:4:5
|
3 | let score = &scores[0];
| ------ immutable borrow occurs here
4 | scores.push(4);
| ^^^^^^ mutable borrow occurs here
5 | }
| - immutable borrow ends here
Tuy nhiên, một con người trivially có thể thấy rằng ví dụ này là quá bảo thủ: score
là không bao giờ sử dụng ! Vấn đề là sự vay mượn của scores
by score
là từ vựng - nó kéo dài cho đến khi kết thúc khối mà nó được chứa trong đó:
fn main() {
let mut scores = vec![1, 2, 3];
let score = &scores[0];
scores.push(4);
}
Các vòng đời không từ vựng khắc phục điều này bằng cách nâng cao trình biên dịch để hiểu mức độ chi tiết này. Giờ đây, trình biên dịch có thể cho biết chính xác hơn khi nào cần mượn và mã này sẽ biên dịch.
Một điều tuyệt vời về các kiếp sống không từ vựng là một khi được kích hoạt, không ai sẽ nghĩ về chúng . Nó sẽ đơn giản trở thành "những gì Rust làm" và mọi thứ (hy vọng) sẽ hoạt động.
Tại sao lại được phép tồn tại từ vựng?
Rust nhằm mục đích chỉ cho phép biên dịch các chương trình an toàn đã biết. Tuy nhiên, không thể chỉ cho phép chính xác các chương trình an toàn và từ chối các chương trình không an toàn. Cuối cùng, Rust đã sai lầm ở khía cạnh bảo thủ: một số chương trình an toàn bị từ chối. Những cuộc đời hợp pháp là một ví dụ về điều này.
Các vòng đời Lexical dễ triển khai hơn nhiều trong trình biên dịch vì kiến thức về các khối là "tầm thường", trong khi kiến thức về luồng dữ liệu thì ít hơn. Trình biên dịch cần được viết lại để giới thiệu và sử dụng "biểu diễn trung gian cấp trung gian" (MIR) . Sau đó, trình kiểm tra mượn (hay còn gọi là "loanck") phải được viết lại để sử dụng MIR thay vì cây cú pháp trừu tượng (AST). Sau đó, các quy tắc của công cụ kiểm tra khoản vay phải được tinh chỉnh để trở nên chi tiết hơn.
Các vòng đời từ vựng không phải lúc nào cũng cản trở lập trình viên và có nhiều cách làm việc xung quanh các vòng đời từ vựng khi chúng xảy ra, ngay cả khi chúng gây phiền nhiễu. Trong nhiều trường hợp, điều này liên quan đến việc thêm dấu ngoặc nhọn hoặc giá trị boolean. Điều này cho phép Rust 1.0 xuất xưởng và hữu ích trong nhiều năm trước khi các vòng đời không từ vựng được triển khai.
Điều thú vị là, một số mẫu tốt nhất định đã được phát triển do có từ vựng lâu đời. Các ví dụ điển hình với tôi là các entry
mô hình . Mã này không thành công trước thời gian tồn tại không từ vựng và được biên dịch với nó:
fn example(mut map: HashMap<i32, i32>, key: i32) {
match map.get_mut(&key) {
Some(value) => *value += 1,
None => {
map.insert(key, 1);
}
}
}
Tuy nhiên, mã này không hiệu quả vì nó tính toán hàm băm của khóa hai lần. Giải pháp được tạo ra vì thời gian tồn tại của từ vựng ngắn hơn và hiệu quả hơn:
fn example(mut map: HashMap<i32, i32>, key: i32) {
*map.entry(key).or_insert(0) += 1;
}
Cái tên "những kiếp sống không từ vựng" nghe không hợp với tôi
Thời gian tồn tại của một giá trị là khoảng thời gian mà giá trị đó vẫn ở một địa chỉ bộ nhớ cụ thể (xem Tại sao tôi không thể lưu trữ một giá trị và một tham chiếu đến giá trị đó trong cùng một cấu trúc để được giải thích lâu hơn). Tính năng được gọi là thời gian tồn tại không từ vựng không thay đổi thời gian tồn tại của bất kỳ giá trị nào, vì vậy nó không thể làm cho thời gian tồn tại không từ vựng. Nó chỉ làm cho việc theo dõi và kiểm tra các khoản vay của các giá trị đó chính xác hơn.
Tên chính xác hơn cho đối tượng địa lý có thể là "từ mượn không có từ vựng ". Một số nhà phát triển trình biên dịch đề cập đến "Vayck dựa trên MIR".
Kiếp phi từ vựng không bao giờ có ý định trở thành một "người dùng phải đối mặt với" tính năng, cho mỗi gia nhập . Hầu hết chúng đã lớn dần lên trong tâm trí của chúng ta vì những nét vẽ nhỏ mà chúng ta nhận được từ sự vắng mặt của chúng. Tên của họ chủ yếu nhằm mục đích phát triển nội bộ và việc thay đổi nó cho mục đích tiếp thị không bao giờ được ưu tiên.
Vâng, nhưng làm thế nào để tôi sử dụng nó?
Trong Rust 1.31 (phát hành vào ngày 12 tháng 12 năm 2018), bạn cần chọn tham gia phiên bản Rust 2018 trong Cargo.toml của mình:
[package]
name = "foo"
version = "0.0.1"
authors = ["An Devloper <an.devloper@example.com>"]
edition = "2018"
Kể từ phiên bản Rust 1.36, phiên bản Rust 2015 cũng cho phép các vòng đời không từ vựng.
Việc triển khai các vòng đời không từ vựng hiện tại đang ở "chế độ di chuyển". Nếu bộ kiểm tra mượn NLL vượt qua, quá trình biên dịch sẽ tiếp tục. Nếu không, công cụ kiểm tra khoản vay trước đó sẽ được gọi. Nếu trình kiểm tra mượn cũ cho phép mã, một cảnh báo sẽ được in, thông báo cho bạn biết rằng mã của bạn có khả năng bị hỏng trong phiên bản Rust trong tương lai và cần được cập nhật.
Trong các phiên bản Rust hàng đêm, bạn có thể chọn tham gia sự cố bị cưỡng chế thông qua cờ tính năng:
#![feature(nll)]
Bạn thậm chí có thể chọn tham gia phiên bản thử nghiệm của NLL bằng cách sử dụng cờ trình biên dịch -Z polonius
.
Một mẫu các vấn đề thực tế được giải quyết bởi các kiếp sống không từ vựng