web-dev-qa-db-fra.com

Comment implémenter Copy and Clone pour un type contenant une chaîne?

J'ai un enum dans Rust qui a une valeur qui prend une String. Ceci peut être démontré avec cet exemple simple:

#[derive(Clone, Copy)]
enum Simple {
    Error(String),
    Okay,
    Foo([u32; 5]),
}

fn main() {
    let x = Simple::Error(String::from("blah"));
    let y = x.clone();
}

La valeur enum Foo ci-dessus représente environ 10 autres enums que j'utilise et qui prennent des types ou des tableaux pouvant être copiés. Le compilateur ne semble pas s'en plaindre, seulement la Error(String) qui provoque ceci:

error[E0204]: the trait `Copy` may not be implemented for this type
 --> src/main.rs:1:17
  |
1 | #[derive(Clone, Copy)]
  |                 ^^^^
2 | enum Simple {
3 |     Error(String),
  |           ------ this field does not implement `Copy`

Pour une raison quelconque, String n'est pas copiable. Je ne comprends pas ça. Comment implémenter Clone pour une énumération pour le seul type qui rencontre un problème lors de l'utilisation de l'impl par défaut pour le reste?

12
locka

Copie

Copy désigne les types pour lesquels faire une copie au niveau du bit crée une instance valide sans invalider l'instance d'origine.

Ce n'est pas vrai pour String , parce que String contient un pointeur sur les données de chaîne sur le segment de mémoire et suppose qu'il est le propriétaire unique de ces données. Lorsque vous supprimez une String, les données du tas sont libérées. Si vous aviez fait une copie au niveau du bit d'une String, les deux instances essaieraient de désallouer le même bloc de mémoire, ce qui est comportement non défini.

Puisque String n'implémente pas Copy, votre enum ne peut pas implémenter Copy non plus car le compilateur impose que les types Copy soient composés uniquement de données membres Copy.

Cloner

Clone fournit simplement une méthode clone standard, et il appartient à chaque implémenteur de décider de la façon dont elle doit être mise en œuvre. String implémente Clone, de sorte que vous pouvez mettez #[derive(Clone)] sur votre enum.

23
Francis Gagné

J'ai fait quelques recherches pour voir à quoi ressemblerait une implémentation manuelle. Je suis venu avec cela, mais gardez à l'esprit que vous pouvez aussi faire #[derive(Clone)] comme indiqué ailleurs et le compilateur le fera pour vous.

enum Simple {
    Error(String),
    Okay,
    Foo([u32; 5]),
}

impl Clone for Simple {
    fn clone(&self) -> Simple {
        match self {
            Error(a) => Error(a.to_string()),
            Okay => Okay,
            Foo(a) => Foo(a.clone()),
        }
    }
}
0
Brandon