web-dev-qa-db-fra.com

Dans Rust, quelle est la différence entre clone () et to_ned ()?

Dans Rust, Clone est un trait qui spécifie la méthode clone (et clone_from). Certains traits, comme StrSlice et CloneableVector spécifient un to_owned fn. Pourquoi une mise en œuvre aurait-elle besoin des deux? Quelle est la différence?

J'ai fait une expérience avec les chaînes Rust, qui ont les deux méthodes, et cela démontre qu'il y a une différence, mais je ne la comprends pas:

fn main() {
    test_clone();
    test_to_owned();
}

// compiles and runs fine    
fn test_clone() {
    let s1: &'static str = "I am static";
    let s2 = "I am boxed and owned".to_string();

    let c1 = s1.clone();
    let c2 = s2.clone();

    println!("{:?}", c1);
    println!("{:?}", c2);

    println!("{:?}", c1 == s1);  // prints true
    println!("{:?}", c2 == s2);  // prints true
}

fn test_to_owned() {
    let s1: &'static str = "I am static";
    let s2 = "I am boxed and owned".to_string();

    let c1 = s1.to_owned();
    let c2 = s2.to_owned();

    println!("{:?}", c1);
    println!("{:?}", c2);

    println!("{:?}", c1 == s1);   // compile-time error here (see below)
    println!("{:?}", c2 == s2);
}

L'erreur de temps de compilation pour le to_owned exemple est:

error: mismatched types: expected `~str` but found `&'static str` 
(str storage differs: expected `~` but found `&'static `)
clone.rs:30     println!("{:?}", c1 == s1);

Pourquoi le premier exemple fonctionnerait-il mais pas le second?

38
quux00

.clone() renvoie son récepteur. clone() sur un &str renvoie un &str. Si vous voulez un String, vous avez besoin d'une méthode différente, qui dans ce cas est .to_owned().

Pour la plupart des types, clone() est suffisant car il n'est défini que sur le type sous-jacent et non sur le type de référence. Mais pour str et [T], clone() est implémenté sur le type de référence (&str et &[T]), et donc il a le mauvais type. Il est également implémenté sur les types possédés (String et Vec<T>), et dans ce cas, clone() renverra une autre valeur possédée.

Votre premier exemple fonctionne car c1 et s1 (et c2 et s2) ont les mêmes types. Votre deuxième exemple échoue car ce n'est pas le cas (c1 est String tandis que s1 est &str). C'est un parfait exemple de la raison pour laquelle des méthodes distinctes sont nécessaires.


Depuis la version actuelle de Rust, les deux compilent maintenant, mais dans test_clone()c1 est un String et dans test_to_owned() c'est un &str. Je suis presque sûr qu'il se compile comme Rust est maintenant plus indulgent à propos du référencement automatique et du déréférencement des valeurs. Dans cet exemple particulier, je crois que la ligne c1 == s1 est compilée comme si elle disait &*c1 == s1. Si vous souhaitez prouver les types impliqués, vous pouvez ajouter une erreur de type délibérée, telle que let _: i32 = c1; et le message d'erreur affichera le type.

34
Lily Ballard