web-dev-qa-db-fra.com

Pourquoi le retour de «Self» dans le trait fonctionne-t-il, mais le retour de «Option <Self>» nécessite «Sized»?

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.)

13
Lukas Kalbertodt

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 avec Self n'étant pas Sized? Parce que s'il n'y a aucun moyen de le faire, je ne vois pas l'intérêt de permettre de retourner Self sans un Sized 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:

  1. La méthode n'a pas de récepteur :

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.

  1. La méthode fait référence au type Self dans ses arguments ou type de retour :

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);
}
1
ljedrz