Обычно, если библиотека имеет универсальный тип 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
for 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
указатель и кортежи. И тестируя их все (пожалуйста, скажите, если этот код не имеет смысла), кажется, что ссылки являются единственными фундаментальными примитивными типами . Интересно. Мне было бы интересно узнать причину, почему другие нет, особенно необработанные указатели. Но это не предмет этого вопроса, я полагаю.