paint-brush
Tham chiếu có thể thay đổi hoặc Biến có thể thay đổi? Giải mã mut-mut-mut của Rusttừ tác giả@charnog
907 lượt đọc
907 lượt đọc

Tham chiếu có thể thay đổi hoặc Biến có thể thay đổi? Giải mã mut-mut-mut của Rust

từ tác giả Denis Gonchar5m2023/09/23
Read on Terminal Reader

dài quá đọc không nổi

Sự an toàn về bộ nhớ của Rust bắt nguồn từ cơ chế sở hữu và mượn của nó. Bài viết này đi sâu vào sự phức tạp của từ khóa mut, phân biệt giữa các tham chiếu có thể thay đổi và các biến có thể thay đổi. Điểm mấu chốt? Không phải tất cả các biến đổi đều được tạo ra như nhau: các tham chiếu có thể thay đổi cho phép thay đổi giá trị được trỏ tới, trong khi các biến có thể thay đổi có tham chiếu có thể thay đổi vị trí chúng trỏ tới. Một thủ thuật trực quan hữu ích bằng cách sử dụng dấu = sẽ đơn giản hóa việc hiểu những khác biệt này.
featured image - Tham chiếu có thể thay đổi hoặc Biến có thể thay đổi? Giải mã mut-mut-mut của Rust
Denis Gonchar HackerNoon profile picture

giới thiệu

Mặc dù Rust cung cấp cuốn sách Rust nhưng tôi vẫn gặp khó khăn trong việc hiểu từ khóa mut . Tôi tự hỏi: "Có phải chỉ mình tôi gặp vấn đề này không?" Một tìm kiếm nhanh trên Google đã xác nhận rằng tôi không đơn độc.


Vì vậy, tôi quyết định viết bài này để giải thích chi tiết về từ khóa mut . Bài viết này dành cho những người sử dụng các ngôn ngữ cấp cao như Python hoặc JavaScript.

Các biến

Để tạo một biến không thể thay đổi trong Rust, chỉ cần viết let x = 1337 . Nó đơn giản. Nếu bạn muốn tạo một biến có thể thay đổi sau này, chỉ cần thêm từ khóa mut sau let . Rust có một quy ước hữu ích khuyến khích sự rõ ràng về ý định.


Việc thêm từ khóa mut sẽ thông báo cho người khác rằng biến này sẽ được sửa đổi ở một nơi khác trong mã. Được rồi.


Hãy hình dung nó. Hai biến ở đây, let mut x = 1337let y = 42 .

Tên đầu tiên; thứ hai - loại, thứ ba - giá trị, thứ tư - cờ mut.

Tài liệu tham khảo

Hiện tại, mọi thứ đều đơn giản. Tuy nhiên, mọi thứ bắt đầu trở nên khó khăn một chút khi sử dụng tham chiếu mut . Hãy tạo ra một số.

 let mut x = 1337; let y = 42; let x_ref = &mut x; let y_ref = &y;


Tôi đã tạo hai tài liệu tham khảo (hoặc "mượn" theo Rust). Một trong số đó là tham chiếu có thể thay đổi và tham chiếu còn lại là tham chiếu chỉ đọc. Hãy tạo một kế hoạch cho điều đó một lần nữa.


Trong sơ đồ đã cho, tôi có 4 biến, 2 trong số đó là tham chiếu. Cả hai biến tham chiếu đều không thay đổi và không có từ khóa mut sau let , điều đó có nghĩa là tôi không thể thay đổi nội dung chúng trỏ tới. Tuy nhiên, tôi vẫn có thể thay đổi giá trị mà họ tham chiếu.

 *x_ref = 777;


Nếu bạn viết điều này, trình biên dịch Rust sẽ không phàn nàn và giá trị của x (không phải chính ref) sẽ thay đổi thành 777 . Tuy nhiên, có một hình vuông màu đỏ trên sơ đồ cho biết x_ref thiếu tùy chọn có thể thay đổi. Vậy tại sao tôi có thể thay đổi giá trị mà nó đề cập đến?


Hãy quay lại sơ đồ cho let x_ref = &mut x .


Khối màu trắng đầu tiên chứa tên: x_ref . Cái thứ hai thông báo cho tôi về loại được lưu trữ trong biến đó. Ở dạng hoàn chỉnh, không có bất kỳ chú thích kiểu ẩn nào, tôi có thể viết như sau:

 let x_ref: &mut i32 = &mut x;


Tôi có thể hiểu điều này là: hãy tạo một biến bất biến có tên x_ref sẽ chứa tham chiếu có thể thay đổi thành i32 và khởi tạo nó ngay lập tức với tham chiếu có thể thay đổi thành giá trị i32 trong biến x .


Điều này có nghĩa là tôi có thể sửa đổi giá trị mà nó trỏ tới, nhưng tôi không thể thay đổi giá trị (hoặc địa chỉ) của tham chiếu. Nói cách khác, tôi không thể viết một cái gì đó như:

 let x_ref: &mut i32 = &mut x; let mut z = 0; x_ref = &mut z; // Not allowed!


Về mặt sơ đồ, tôi muốn thay đổi hướng mũi tên chỉ vào khối mã ở trên. Tuy nhiên, ngay cả khi biến z có thể thay đổi, tôi không thể thay đổi mũi tên vì vấn đề nằm ở tính bất biến của chính x_ref .


Để thay đổi hướng mũi tên, tôi cần sửa đổi địa chỉ được lưu trong biến x_ref . Tuy nhiên, tôi không thể làm điều này vì biến này không thể thay đổi được.


Hãy làm nó!

 let mut x: i32 = 1337; let mut x_ref: &mut i32 = &mut x; // I've added mut before x_ref let mut z = 0; x_ref = &mut z; // Allowed!


Có quá nhiều trường hợp mut ở đây xung quanh x_ref , phải không? Hãy mô tả chúng.


  1. let mut x_ref : Tôi đang tạo một biến có thể thay đổi có tên x_ref , có nghĩa là tôi có thể thay đổi giá trị của nó sau này.


  2. &mut i32 : Tôi tuyên bố rằng biến sẽ chứa (các) tham chiếu có thể thay đổi đối với một số giá trị thuộc loại i32 .


  3. &mut x : Tôi đang mượn (lấy tham chiếu đến) biến x .


Sau đó, tôi tạo một biến có tên z và gán cho nó giá trị 0 . Sau đó, khi tôi viết x_ref = &mut z , tôi đã chỉ ra rằng tôi hiểu x_ref là một biến có thể thay đổi chỉ có thể chứa các tham chiếu đến các giá trị i32 .


Vì loại của zi32 nên tôi có thể gán địa chỉ của nó cho biến x_ref . Để lấy địa chỉ của z , tôi đã sử dụng cú pháp &mut z .


Kế hoạch.

Thủ thuật tinh thần

Hãy xem = trong câu lệnh, nó có thể trông hơi rõ ràng một chút, nhưng…

 let mut x_ref = &mut x;


… Tôi thấy nó như một dải phân cách (đặc biệt nếu bạn xoay nó 90 độ) để chia câu lệnh thành hai câu lệnh phụ: trái và phải. Phía bên trái cung cấp thông tin về chính biến đó, trong khi phía bên phải cho chúng ta biết về giá trị .


Khi tôi sử dụng toán tử quy định * để thay đổi giá trị...

 *x_ref = 100;

... Tôi không thay đổi giá trị của biến x_ref . Thay vào đó, tôi đang thay đổi giá trị mà x_ref đang tham chiếu.

Tài liệu tham khảo không thể thay đổi

Tôi đã sử dụng mut thường xuyên trước đây. Nếu tôi bỏ qua một số trong số đó thì sao?

 let i = 1; let j = 2; let mut k = &i;


Tôi có thể thay đổi giá trị của i ở đây không? Sử dụng kỹ thuật chia, câu trả lời khá đơn giản. Tôi có thể thay đổi giá trị của k (tôi thấy mut ở bên trái), nhưng giá trị (bên phải) là một tham chiếu bất biến đối với i (không có mut ở đây).


Vì thế…

 let i = 1; let j = 2; let mut k = &i; k = &j; // This is legal. *k = 3; // This is not.


Kế hoạch.

Phần kết luận

Trong bài viết này, chúng tôi đã mổ xẻ các sắc thái của từ khóa mut và các tài liệu tham khảo. Hãy nhớ rằng có sự khác biệt giữa tham chiếu có thể thay đổibiến có thể thay đổi chứa tham chiếu. Thủ thuật của chúng tôi?


Sử dụng dấu = làm dấu chia để hiểu rõ hơn các phép gán trong Rust. Hình dung đơn giản này có thể làm sáng tỏ nhiều nhầm lẫn.


Chúc mừng mã hóa!