Зазвичай, якщо бібліотека має загальний тип Foo<T>
, ящики нижче за течією не можуть реалізувати на ній ознаки, навіть якщо T
це якийсь локальний тип. Наприклад,
( 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)
}
}
Для конкретного прикладу, який працює на дитячому майданчику (тобто дає помилку),
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)
}
}
(дитячий майданчик)
Це, як правило, дає змогу автору ящиків додавати (бланкетне) реалізацію ознак, не розбиваючи ящики вниз за течією. Це чудово у випадках, коли спочатку не визначено, що тип повинен реалізувати певну ознаку, але згодом стає зрозуміло, що він повинен. Наприклад, у нас може бути якийсь числовий тип, який спочатку не реалізує ознаки num-traits
. Ці риси можна буде додати пізніше, не потребуючи перерви.
Однак у деяких випадках автор бібліотеки хоче, щоб ящики вниз за течією могли самі реалізувати риси. Тут розміщується #[fundamental]
атрибут. Якщо розміщуватися на типі, будь-яка ознака, що не реалізована в даний час для цього типу, не буде реалізована (забороняючи переломну зміну). В результаті ящики нижче за течією можуть реалізовувати риси для цього типу, якщо параметр типу є локальним (є деякі складні правила для визначення того, які параметри типу для цього враховуються). Оскільки фундаментальний тип не реалізує задану ознаку, цю ознаку можна вільно реалізувати, не викликаючи проблем узгодженості.
Наприклад, Box<T>
позначений #[fundamental]
, тому працює наступний код (схожий на Rc<T>
версію вище). Box<T>
не реалізовує Default
(якщо не T
реалізує Default
), тому ми можемо припустити, що це не буде в майбутньому, оскільки Box<T>
є основним. Зверніть увагу , що реалізація Default
для Bar
викличе проблеми, так як тоді Box<Bar>
вже інвентарю Default
.
struct Bar;
impl Default for Box<Bar> {
fn default() -> Self {
Box::new(Bar)
}
}
(дитячий майданчик)
З іншого боку, ознаки також можна позначати #[fundamental]
. Це має подвійне значення для фундаментальних типів. Якщо будь-який тип в даний час не реалізує основної ознаки, можна припустити, що цей тип не буде реалізовувати його в майбутньому (знову ж таки, забороняючи переломну зміну). Я не зовсім впевнений, як це використовується на практиці. У коді (зв'язаний нижче) FnMut
позначається основним із зауваженням, що він потрібен для регулярного вираження (щось про &str: !FnMut
). Я не міг знайти, де він використовується в regex
ящику або якщо він використовується в іншому місці.
Теоретично, якби Add
ознака була позначена фундаментальною (про що йшлося), це можна було б використовувати для впровадження доповнення між речами, у яких її ще немає. Наприклад, додавання [MyNumericType; 3]
(в точку), що може бути корисним у певних ситуаціях (звичайно, [T; N]
фундаментальне також дозволить це).
Примітивні основні типи &T
, &mut T
(див тут для демонстрації всіх загальних примітивних типів). У стандартній бібліотеці, Box<T>
а Pin<T>
також відзначені в якості основних.
Основні риси в стандартній бібліотеці Sized
, Fn<T>
, FnMut<T>
, FnOnce<T>
і Generator
.
Зауважте, що #[fundamental]
атрибут наразі нестабільний. Проблема відстеження - випуск №29635 .
&T
,&mut T
,*const T
,*mut T
,[T; N]
,[T]
,fn
покажчик і кортежі. І протестуючи їх усіх (скажіть, будь ласка, якщо цей код не має сенсу), здається, що посилання є єдиними основними примітивними типами . Цікаво. Мені було б цікаво знати міркування, чому інші не є, особливо сировинні покажчики. Але я думаю, що це не обсяг цього питання.