References và Borrowing trong Rust

Tram Ho

Tiếp về vấn đề Ownership trong Rust, nhưng đã nói, sẽ rất mất công khi chúng ta muốn một hàm nhận tham số đầu vào là một biến kiểu String mà lại muốn bảo toàn Ownership cho biến đó, thật may là Rust đã giải quyết vấn đề này bằng References and Borrowing.

Immutable reference

Xét ví dụ sau đây:

Ở đây có ký & là biểu thị phép tham chiếu (Reference), ngoài ra trong Rust còn có * là biểu thị cho Dereference (cái từ này không biết dịch là gì, phản tham chiếu hả? Nghe ổn hông).

Minh hoạ các biến của đoạn code này trên StackHeap như sau:

Kiến thức cũ về String thì bộ 3 ptr len capacity của s1 được lưu trên Stack, giá trị "hello" được lưu trên Heap chắc vẫn nhớ ha.

Còn đối với s, hiểu nôm na là trước khi chạy logic của hàm calculate_lenght() ta có một bước s = &s1, nghĩa là s tham chiếu (reference) đến s1, mà để ghi nhớ việc tham chiếu đó thì chúng ta cần lưu lại giá trị con trỏ trỏ đến s1 cho s, mà kích thước cần dùng để lưu một ptr là cố định cho nên s cũng được lưu trên Stack.

Phép tham chiếu không chuyển ownership của giá "hello" từ s1 sang cho s, chỉ đơn giản là s đang tham chiếu đến ss1 đang sở hữu "hello" nên có thể hiểu là s đang mang giá trị "hello".

Do đó, sau khi kết thúc hàm calculate_length(), nghĩa là s đã ra khỏi scope nó, s sẽ bị drop nhưng sẽ không ảnh hưởng gì đến ownership của s1 đối với "hello" nên lệnh println!("The length of '{}' is {}.", s1, len) hoàn toàn hợp lệ.

Khi ta muốn đọc giá trị của s, Compiler sẽ xác định được nó là một tham chiếu nên sẽ truy đến tận cùng giá trị thật đang nằm trên Heap. Do đó, khi gọi pritnln!("{}", s); nó sẽ in ra "hello" chứ không phải in ra thông tin dạng {ptr: xxx, len: 5, capacity: 5}.

Đoạn này có ai thắc mắc là tại sao cùng lưu trên Stack nhưng Compiler lại biết được s là một tham chiếu, còn s1 là một biến đang sở hữu một giá trị trên Heap không? Mình đoán là do khi nhìn thấy cụm ptr len capacity đi với nhau thì biết ngay là một biến đang sở hữu một giá trị, còn khi chỉ nhìn thấy mỗi ptr thì biết nó là tham chiếu.

Mutable reference

Ở trên chỉ là Immutable reference, nghĩa là tham chiếu đến một giá trị và chỉ có quyền đọc giá trị đấy, không có quyền sửa đổi, vậy muốn sửa đổi thì làm thế nào.

Xét ví dụ sau:

Lỗi liền nha:

Như đã nói ở bài trước, các biến trong Rust mặc định immutable (bất biến), lạ ha, biến mà lại bất biến, muốn một biến khả biến thì phải thêm mut.

Đoạn code sau hoàn hợp lệ:

  • Đầu tiên s được khai báo, sowner của giá trị hello
  • Gọi hàm change() với &mut s: lúc này hiểu là some_string = &mut s, giá trị mà some_string mang là hello
  • some_string.push_str(", world"): push thêm ", world" và giá trị mà some_string đang mang, lúc này thành "hello, world"
  • Tuy nhiên giá trị vừa được cập nhật đó vốn luôn thuộc về s, cho nên khi kết thúc hàm thì some_string bị drop nhưng "hello, world" vẫn là giá trị của s

Nguyên tắc khi dùng reference

Có một nguyên tắc duy nhất là: khi đang có một tham chiếu &mut đang tồn tại thì không một tham chiếu nào khác kể cả &mut hay & được phép tồn tại.

Lỗi: Khi cả r1r2 đều muốn thay đổi giá trị của s thì s biết nghe đứa nào.

Lỗi: r1r2 đang tham chiếu đến sr3 lại thay đổi giá trị của s thì r1, r2 biết đường nào mà lần

Nhưng nếu sửa thành:

thì chương trình hoàn toàn chạy được, do Compiler xác định được r1r2 đã không còn được dùng ở bất cứ đâu kể từ dòng khai báo của r3.

Cũng tương tự, đoạn code sau hoàn toàn hợp lệ:

Dangling References

Buồn ngủ quá mấy bồ ơi, mai viết tiếp, G9!

Dậy rồi nè!

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo