Cette définition de trait compile bien:
trait Works {
fn foo() -> Self;
}
Cependant, cela conduit à une erreur:
trait Errors {
fn foo() -> Option<Self>;
}
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/lib.rs:6:5
|
6 | fn foo() -> Option<Self>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Self`
= note: to learn more, visit <https://doc.Rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where Self: std::marker::Sized` bound
= note: required by `std::option::Option`
Avec le : Sized
supertrait lié, ça marche.
Je sais que le type Self
dans les traits n'est pas automatiquement lié à Sized
. Et je comprends que Option<Self>
ne peut pas être retourné (via la pile) à moins qu'il ne soit dimensionné (ce qui nécessite à son tour Self
pour être dimensionné). Cependant, il en irait de même pour Self
comme type de retour, non? Il ne peut pas non plus être stocké sur la pile sauf s'il est dimensionné.
Pourquoi la première définition de trait ne déclenche-t-elle pas déjà cette erreur?
(Cette question est liée, mais elle ne répond pas à ma question exacte - sauf si je ne l'ai pas comprise.)
Le problème avec Option
n'est que la pointe de l'iceberg et les autres l'ont déjà expliqué; Je voudrais développer votre question dans le commentaire :
est-il possible d'implémenter fn
foo() -> Self
avecSelf
n'étant pasSized
? Parce que s'il n'y a aucun moyen de le faire, je ne vois pas l'intérêt de permettre de retournerSelf
sans unSized
lié.
Cette méthode rend en effet impossible (au moins actuellement) d'utiliser le trait comme un objet trait en raison de 2 problèmes:
Les méthodes qui ne prennent pas de paramètre
self
ne peuvent pas être appelées car il n'y aura aucun moyen d'obtenir un pointeur vers la table de méthodes pour elles.
Cela rend le trait non sûr pour les objets, c'est-à-dire qu'il est impossible de créer un objet trait à partir de celui-ci.
Vous pouvez toujours parfaitement l'utiliser pour d'autres choses, cependant:
trait Works {
fn foo() -> Self;
}
#[derive(PartialEq, Debug)]
struct Foo;
impl Works for Foo {
fn foo() -> Self {
Foo
}
}
fn main() {
assert_eq!(Foo::foo(), Foo);
}