Một loại cơ bản của người Viking trong Rust là gì?


37

Ở đâu đó tôi đã chọn thuật ngữ "loại cơ bản" (và thuộc tính của nó #[fundamental]) và bây giờ tôi muốn tìm hiểu thêm về nó. Tôi mơ hồ nhớ nó là về việc thư giãn các quy tắc kết hợp trong một số tình huống. Và tôi nghĩ rằng các loại tài liệu tham khảo là loại cơ bản như vậy.

Thật không may, tìm kiếm trên web đã không mang lại cho tôi rất xa. Tài liệu tham khảo Rust không đề cập đến nó (theo như tôi có thể thấy). Tôi chỉ tìm thấy một vấn đề về việc tạo ra các loại cơ bảnRFC đã giới thiệu thuộc tính . Tuy nhiên, RFC có một đoạn duy nhất về các loại cơ bản:

  • Một #[fundamental]loại Foolà một trong đó thực hiện một ngụ ý chăn Foolà một sự thay đổi đột phá. Theo mô tả, &&mutlà cơ bản. Thuộc tính này sẽ được áp dụng Box, làm cho Box hành vi giống như &&mutliên quan đến sự gắn kết.

Tôi thấy từ ngữ khá khó hiểu và có cảm giác như tôi cần kiến ​​thức chuyên sâu về RFC đầy đủ để hiểu bit này về các loại cơ bản. Tôi đã hy vọng ai đó có thể giải thích các loại cơ bản bằng các thuật ngữ có phần đơn giản hơn (tất nhiên không đơn giản hóa quá nhiều). Câu hỏi này cũng sẽ phục vụ như một phần kiến ​​thức dễ tìm.

Để hiểu các loại cơ bản, tôi muốn trả lời những câu hỏi này (ngoài câu hỏi chính là "chúng là gì?", Tất nhiên):

  • Các loại cơ bản có thể làm nhiều hơn những loại không cơ bản?
  • Tôi có thể, với tư cách là một tác giả thư viện, bằng cách nào đó đánh dấu một số loại của tôi là #[fundamental]?
  • Những loại từ ngôn ngữ cốt lõi hoặc thư viện tiêu chuẩn là cơ bản?

Câu trả lời:


34

Thông thường, nếu một thư viện có loại chung Foo<T>, các thùng phía dưới không thể thực hiện các đặc điểm trên đó, ngay cả khi Tlà một loại cục bộ. Ví dụ,

( crate_a)

struct Foo<T>(pub t: T)

( crate_b)

use crate_a::Foo;

struct Bar;

// This causes an error
impl Clone for Foo<Bar> {
    fn clone(&self) -> Self {
        Foo(Bar)
    }
}

Đối với một ví dụ cụ thể hoạt động trên sân chơi (nghĩa là có lỗi),

use std::rc::Rc;

struct Bar;

// This causes an error
// error[E0117]: only traits defined in the current crate
// can be implemented for arbitrary types
impl Default for Rc<Bar> {
    fn default() -> Self {
        Rc::new(Bar)
    }
}

(sân chơi)


Điều này thường cho phép tác giả thùng thêm (triển khai) các đặc điểm mà không phá vỡ các thùng phía dưới. Điều đó thật tuyệt vời trong trường hợp ban đầu không chắc chắn rằng một loại sẽ thực hiện một đặc điểm cụ thể, nhưng sau đó nó trở nên rõ ràng rằng nó nên. Ví dụ: chúng ta có thể có một số loại số ban đầu không triển khai các đặc điểm từ đó num-traits. Những đặc điểm này có thể được thêm vào sau mà không cần thay đổi đột phá.

Tuy nhiên, trong một số trường hợp, tác giả thư viện muốn các thùng phía dưới có thể tự thực hiện các đặc điểm. Đây là nơi #[fundamental]thuộc tính xuất hiện. Khi được đặt trên một loại, bất kỳ đặc điểm nào hiện không được triển khai cho loại đó sẽ không được thực hiện (cấm thay đổi vi phạm). Kết quả là, các thùng hạ lưu có thể thực hiện các đặc điểm cho loại đó miễn là tham số loại là cục bộ (có một số quy tắc phức tạp để quyết định loại tham số nào được tính cho điều này). Vì loại cơ bản sẽ không thực hiện một đặc điểm nhất định, nên đặc điểm đó có thể được thực hiện tự do mà không gây ra vấn đề kết hợp.

Ví dụ, Box<T>được đánh dấu #[fundamental], do đó đoạn mã sau (tương tự như Rc<T>phiên bản trên) hoạt động. Box<T>không thực hiện Default(trừ khi Tthực hiện Default) vì vậy chúng tôi có thể cho rằng nó sẽ không trong tương lai vì Box<T>là cơ bản. Lưu ý rằng việc thực hiện Defaultcho Barsẽ gây ra vấn đề, kể từ đó Box<Bar>đã cụ Default.

struct Bar;

impl Default for Box<Bar> {
    fn default() -> Self {
        Box::new(Bar)
    }
}

(sân chơi)


Mặt khác, các đặc điểm cũng có thể được đánh dấu bằng #[fundamental]. Điều này có một ý nghĩa kép cho các loại cơ bản. Nếu bất kỳ loại nào hiện không thực hiện một đặc điểm cơ bản, có thể giả định rằng loại đó sẽ không thực hiện nó trong tương lai (một lần nữa, ngăn chặn một sự thay đổi đột phá). Tôi không chắc chắn chính xác làm thế nào điều này được sử dụng trong thực tế. Trong mã (được liên kết bên dưới), FnMutđược đánh dấu cơ bản với ghi chú rằng nó cần thiết cho regex (một cái gì đó về &str: !FnMut). Tôi không thể tìm thấy nơi nó được sử dụng trong regexthùng hoặc nếu nó được sử dụng ở nơi khác.

Về lý thuyết, nếu Addđặc điểm được đánh dấu cơ bản (đã được thảo luận) thì điều này có thể được sử dụng để thực hiện bổ sung giữa những thứ chưa có. Ví dụ, thêm [MyNumericType; 3](theo chiều), có thể hữu ích trong một số tình huống nhất định (tất nhiên, làm [T; N]cơ bản cũng sẽ cho phép điều này).


Các loại cơ bản nguyên thủy là &T, &mut T(xem ở đây để trình diễn tất cả các loại nguyên thủy chung). Trong thư viện tiêu chuẩn, Box<T>Pin<T>cũng được đánh dấu là cơ bản.

Các đặc điểm cơ bản trong thư viện chuẩn được Sized, Fn<T>, FnMut<T>, FnOnce<T>Generator.


Lưu ý rằng #[fundamental]thuộc tính hiện không ổn định. Vấn đề theo dõi là vấn đề # 29635 .


1
Câu trả lời chính xác! Về loại nguyên thủy: chỉ có một số ít chung loại nguyên thủy: &T, &mut T, *const T, *mut T, [T; N], [T], fncon trỏ và các bộ. Và kiểm tra tất cả chúng (xin vui lòng cho tôi biết nếu mã này không có ý nghĩa) có vẻ như các tài liệu tham khảo là loại nguyên thủy cơ bản duy nhất . Hấp dẫn. Tôi sẽ quan tâm đến việc biết lý do tại sao những người khác không, đặc biệt là con trỏ thô. Nhưng đó không phải là phạm vi của câu hỏi này, tôi đoán vậy.
Lukas Kalbertodt

1
@LukasKalbertodt Cảm ơn thông tin về các loại nguyên thủy. Tôi đã thêm vào bài kiểm tra của bạn. Đối với lý do về tham chiếu so với con trỏ, hãy xem nhận xét này trong yêu cầu kéo RFC.
SCappella

Tham chiếu không ghi lại các thuộc tính không ổn định, vì vậy đó là lý do tại sao bạn không tìm thấy nó ở đó.
Havvy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.