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