Tập 36: `view` và mượn — Chỉ nhìn, không giữ

Move semantics giải quyết được bài toán giải phóng bộ nhớ an toàn, nhưng nó tạo ra một vấn đề về hiệu năng: việc chuyển nhượng quyền sở hữu liên tục rất tốn kém và bất tiện. Nếu một hàm chỉ muốn đọc nội dung của một buffer để kiểm tra cú pháp, việc bắt nó nhận quyền sở hữu rồi trả lại khiến code trở nên vô cùng cồng kềnh và phát sinh nhiều dòng code boilerplate vô nghĩa.

Chúng tôi cần một cơ chế cho phép đọc tạm thời mà không tốn chi phí. Đó là lúc view[T] ra đời trong Sprint 07.

fn parse(input: view[u8]) -> Ast

view[T] là một góc nhìn read-only vào vùng nhớ được sở hữu bởi một biến khác. Quy tắc của view rất ngặt nghèo: anh chỉ được nhìn, không được ghi, và không được giữ nó lâu hơn thời gian sống của chủ sở hữu thật.

Trong V0, vì chưa có hệ thống lifetime parameters phức tạp dạng 'a như của Rust, chúng tôi phải áp dụng những luật biên giới cực kỳ đơn giản để ngăn chặn lỗi dangling references (con trỏ treo): - View chỉ được tạo ra từ owned local variable trong cùng một scope hàm. - View không được phép thoát (escape) khỏi hàm thông qua giá trị trả về hoặc gán vào một cấu trúc có thời gian sống lâu hơn.

Quy tắc R-VIEW-2 giữ cho các lát cắt mượn này luôn nằm trong ranh giới an toàn. Tuy nhiên, nhược điểm là sự giới hạn này khiến view trong V0 chỉ phù hợp cho các hàm xử lý phẳng (utility functions) hoặc duyệt mảng, chưa thể lưu vào struct làm thành viên lâu dài.

Nhưng đó là một sự thỏa hiệp có tính toán. Khi tôi code thử module parser đầu tiên, việc thiếu lifetime parameter bắt tôi phải copy một số chuỗi con thay vì mượn trực tiếp. Cảm giác lúc đó khá khó chịu, giống như bị xích chân. Nhưng khi nghĩ về việc core checker của mình chỉ cần chưa đầy 500 dòng code để chứng minh sự an toàn mà không cần đến giải thuật mượn phức tạp, tôi thấy sự đánh đổi này hoàn toàn xứng đáng. Lập trình viên DUY học cách sống chung với việc copy dữ liệu trong một số trường hợp phức tạp, đổi lại họ có một mô hình bộ nhớ rõ ràng và dễ hiểu hơn nhiều.


Bình luận (0)

Đang tải bình luận...