Normally, if a library has a generic type Foo<T>
, downstream crates can't implement traits on it, even if T
is some local type. For example,
(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)
}
}
For a concrete example that works on the playground (that is, gives an error),
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)
}
}
(playground)
This normally enables the crate author to add (blanket) implementations of traits without breaking downstream crates. That's great in cases where it isn't initially certain that a type should implement a particular trait, but it later becomes clear that it should. For example, we might have some sort of numeric type that initially doesn't implement the traits from num-traits
. Those traits could be added in later without needing a breaking change.
However, in some cases, the library author wants downstream crates to be able to implement traits themselves. This is where the #[fundamental]
attribute comes in. When placed on a type, any trait not currently implemented for that type won't be implemented (barring a breaking change). As a result, downstream crates can implement traits for that type as long as a type parameter is local (there are some complicated rules for deciding which type parameters count for this). Since the fundamental type won't implement a given trait, that trait can freely be implemented without causing coherence issues.
For example, Box<T>
is marked #[fundamental]
, so the following code (similar to the Rc<T>
version above) works. Box<T>
doesn't implement Default
(unless T
implements Default
) so we can assume that it won't in the future because Box<T>
is fundamental. Note that implementing Default
for Bar
would cause problems, since then Box<Bar>
already implements Default
.
struct Bar;
impl Default for Box<Bar> {
fn default() -> Self {
Box::new(Bar)
}
}
(playground)
On the other hand, traits can also be marked with #[fundamental]
. This has a dual meaning to fundamental types. If any type doesn't currently implement a fundamental trait, it can be assumed that that type won't implement it in the future (again, barring a breaking change). I'm not exactly sure how this is used in practice. In the code (linked below), FnMut
is marked fundamental with the note that it's needed for regex (something about &str: !FnMut
). I couldn't find where it's used in the regex
crate or if it's used elsewhere.
In theory, if the Add
trait were marked fundamental (which has been discussed) this could be used to implement addition between things that don't already have it. For example, adding [MyNumericType; 3]
(pointwise), which could be useful in certain situations (of course, making [T; N]
fundamental would also allow this).
The primitive fundamental types are &T
, &mut T
(see here for a demonstration of all the generic primitive types). In the standard library, Box<T>
and Pin<T>
are also marked as fundamental.
The fundamental traits in the standard library are Sized
, Fn<T>
, FnMut<T>
, FnOnce<T>
and Generator
.
Note that the #[fundamental]
attribute is currently unstable. The tracking issue is issue #29635.