通常,如果库具有泛型类型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)
}
}
(操场)
通常,这可以使板条箱创建者添加(完全)性状实现,而不会破坏下游板条箱。在最初不确定某个类型应实现特定特征的情况下,这很好,但后来变得很清楚,它应该实现。例如,我们可能有某种数字类型,最初并未实现from的特征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
指针和元组。并对其全部进行测试(请告诉我这段代码是否有意义),似乎引用是唯一的基本原始类型。有趣。我很想知道其他人为什么不这样做的原因,尤其是原始指针。我想这不是这个问题的范围。