C ++ có ba cách để truyền tham số cho hàm: theo giá trị, theo tham chiếu lvalue và bằng tham chiếu rvalue. Trong số này, truyền theo giá trị tạo ra quyền sở hữu theo nghĩa là hàm được gọi nhận bản sao của chính nó và chuyển qua tham chiếu giá trị chỉ ra rằng giá trị có thể được tiêu thụ, tức là sẽ không được người gọi sử dụng nữa. Vượt qua tham chiếu lvalue có nghĩa là đối tượng được mượn tạm thời từ người gọi.
Tuy nhiên, những thứ này có xu hướng là các bản dịch theo quy ước và không thể luôn luôn được trình biên dịch kiểm tra. Và bạn có thể vô tình biến một tham chiếu lvalue thành một tham chiếu rvalue bằng cách sử dụng std::move()
. Cụ thể, có ba vấn đề:
Một tham chiếu có thể tồn tại lâu hơn đối tượng mà nó tham chiếu. Hệ thống trọn đời của Rust ngăn chặn điều này.
Có thể có nhiều hơn một tham chiếu có thể thay đổi / không cấu thành bất cứ lúc nào. Công cụ kiểm tra khoản vay của Rust ngăn chặn điều này.
Bạn không thể từ chối tham khảo. Bạn không thể nhìn thấy tại một trang web cuộc gọi cho dù chức năng đó tạo ra một tham chiếu đến đối tượng của bạn, mà không biết chữ ký của chức năng được gọi. Do đó, bạn không thể ngăn chặn các tài liệu tham khảo một cách đáng tin cậy, bằng cách xóa bất kỳ phương thức đặc biệt nào của các lớp cũng như bằng cách kiểm tra trang web cuộc gọi để tuân thủ một số hướng dẫn về phong cách không có tài liệu tham khảo.
Vấn đề suốt đời là về an toàn bộ nhớ cơ bản. Tất nhiên việc sử dụng một tham chiếu khi đối tượng được tham chiếu đã hết hạn là bất hợp pháp. Nhưng rất dễ quên về thời gian tồn tại khi bạn lưu trữ một tham chiếu trong một đối tượng, đặc biệt là khi đối tượng đó tồn tại lâu hơn phạm vi hiện tại. Hệ thống loại C ++ không thể giải thích điều này vì nó hoàn toàn không mô hình hóa tuổi thọ đối tượng.
Con std::weak_ptr
trỏ thông minh không mã hóa ngữ nghĩa sở hữu tương tự như một tham chiếu đơn giản, nhưng yêu cầu đối tượng được tham chiếu được quản lý thông qua a shared_ptr
, tức là được tính tham chiếu. Đây không phải là một sự trừu tượng chi phí bằng không.
Mặc dù C ++ có hệ thống const, nhưng điều này không theo dõi liệu một đối tượng có thể được sửa đổi hay không, nhưng theo dõi xem một đối tượng có thể được sửa đổi thông qua tham chiếu cụ thể đó hay không . Điều đó không cung cấp đủ sự đảm bảo cho các chương trình đồng thời không sợ hãi. Ngược lại, Rust đảm bảo rằng nếu có một tham chiếu có thể thay đổi hoạt động là tham chiếu duy nhất (thì tôi là người duy nhất có thể thay đổi đối tượng này) và nếu có các tham chiếu không thể thay đổi thì tất cả các tham chiếu đến đối tượng đều không thể thay đổi (Trong khi tôi có thể đọc từ đối tượng, không ai có thể thay đổi nó).
Trong C ++, bạn có thể muốn bảo vệ quyền truy cập vào một đối tượng thông qua một con trỏ thông minh với một mutex. Nhưng như đã thảo luận ở trên một khi chúng ta có một tài liệu tham khảo, nó có thể thoát khỏi tuổi thọ dự kiến của nó. Do đó, một con trỏ thông minh như vậy không thể đảm bảo rằng đó là điểm truy cập duy nhất vào đối tượng được quản lý của nó. Một sơ đồ như vậy thực sự có thể hoạt động trong thực tế bởi vì hầu hết các lập trình viên không muốn phá hoại chính họ, nhưng từ quan điểm kiểu hệ thống, điều này vẫn hoàn toàn không có cơ sở.
Vấn đề chung với con trỏ thông minh là chúng là các thư viện nằm trên ngôn ngữ cốt lõi. Tập hợp các tính năng ngôn ngữ cốt lõi cho phép các con trỏ thông minh này, ví dụ như std::unique_ptr
các nhà xây dựng di chuyển. Nhưng họ không thể sửa chữa thiếu sót trong ngôn ngữ cốt lõi. Các khả năng tạo ngầm định các tham chiếu khi gọi một hàm và có các tham chiếu lơ lửng cùng nhau có nghĩa là ngôn ngữ C ++ cốt lõi là không có cơ sở. Không thể giới hạn các tham chiếu có thể thay đổi thành một tham chiếu duy nhất có nghĩa là C ++ không thể đảm bảo an toàn trước các điều kiện chủng tộc với bất kỳ loại đồng thời nào.
Tất nhiên trong nhiều khía cạnh, C ++ và Rust giống nhau hơn là không giống nhau, đặc biệt là về các khái niệm của chúng về thời gian sống của đối tượng được xác định tĩnh. Nhưng trong khi có thể viết các chương trình C ++ chính xác (miễn là không có lập trình viên nào mắc lỗi), Rust đảm bảo tính chính xác liên quan đến các thuộc tính được thảo luận.