Normalement, si une bibliothèque a un type générique Foo<T>
, les caisses en aval ne peuvent pas implémenter de traits dessus, même s'il T
s'agit d'un type local. Par exemple,
( 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)
}
}
Pour un exemple concret qui fonctionne sur le terrain de jeu (c'est-à-dire, donne une erreur),
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)
}
}
(terrain de jeux)
Cela permet normalement à l'auteur de la caisse d'ajouter des mises en œuvre (générales) de traits sans casser les caisses en aval. C'est formidable dans les cas où il n'est pas initialement certain qu'un type doit implémenter un trait particulier, mais il devient plus tard clair que c'est le cas. Par exemple, nous pourrions avoir une sorte de type numérique qui initialement ne met pas en œuvre les traits de num-traits
. Ces traits pourraient être ajoutés plus tard sans avoir besoin d'un changement de rupture.
Cependant, dans certains cas, l'auteur de la bibliothèque souhaite que les caisses en aval soient capables d'implémenter les traits elles-mêmes. C'est là que l' #[fundamental]
attribut entre en jeu. Lorsqu'il est placé sur un type, tout trait non implémenté actuellement pour ce type ne sera pas implémenté (sauf un changement de rupture). Par conséquent, les caisses en aval peuvent implémenter des traits pour ce type tant qu'un paramètre de type est local (il existe des règles compliquées pour décider quels paramètres de type comptent pour cela). Étant donné que le type fondamental n'implémentera pas un trait donné, ce trait peut être librement mis en œuvre sans causer de problèmes de cohérence.
Par exemple, Box<T>
est marqué #[fundamental]
, donc le code suivant (similaire à la Rc<T>
version ci-dessus) fonctionne. Box<T>
n'implémente pas Default
(sauf s'il T
implémente Default
) donc nous pouvons supposer que ce ne sera pas le cas à l'avenir car Box<T>
c'est fondamental. Notez que l'implémentation de Default
for Bar
entraînerait des problèmes, car Box<Bar>
déjà implémente Default
.
struct Bar;
impl Default for Box<Bar> {
fn default() -> Self {
Box::new(Bar)
}
}
(terrain de jeux)
D'un autre côté, les traits peuvent également être marqués avec #[fundamental]
. Cela a une double signification pour les types fondamentaux. Si un type n'implémente pas actuellement un trait fondamental, on peut supposer que ce type ne l'implémentera pas à l'avenir (encore une fois, à moins d'un changement de rupture). Je ne sais pas exactement comment cela est utilisé dans la pratique. Dans le code (lié ci-dessous), FnMut
est marqué fondamental avec la note qu'il est nécessaire pour l'expression régulière (quelque chose à propos &str: !FnMut
). Je n'ai pas pu trouver où il est utilisé dans la regex
caisse ou s'il est utilisé ailleurs.
En théorie, si le Add
trait était marqué comme fondamental (ce qui a été discuté), cela pourrait être utilisé pour implémenter l'addition entre des choses qui ne l'ont pas déjà. Par exemple, l'ajout [MyNumericType; 3]
(point par point), qui pourrait être utile dans certaines situations (bien sûr, rendre [T; N]
fondamental permettrait également cela).
Les types fondamentaux primitifs sont &T
, &mut T
(voir ici pour une démonstration de tous les types primitifs génériques). Dans la bibliothèque standard, Box<T>
et Pin<T>
sont également marqués comme fondamentaux.
Les traits fondamentaux de la bibliothèque standard sont Sized
, Fn<T>
, FnMut<T>
, FnOnce<T>
et Generator
.
Notez que l' #[fundamental]
attribut est actuellement instable. Le problème de suivi est le numéro # 29635 .
&T
,&mut T
,*const T
,*mut T
,[T; N]
,[T]
,fn
pointeur et tuples. Et en les testant tous (dites-moi si ce code n'a pas de sens), il semble que les références soient les seuls types primitifs fondamentaux . Intéressant. Je serais intéressé de savoir pourquoi les autres ne le sont pas, en particulier les pointeurs bruts. Mais ce n'est pas la portée de cette question, je suppose.