J'ai créé une structure à deux éléments Vector
et je veux surcharger l'opérateur +
.
J'ai fait en sorte que toutes mes fonctions et méthodes prennent des références, plutôt que des valeurs, et je veux que l'opérateur +
Fonctionne de la même manière.
impl Add for Vector {
fn add(&self, other: &Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
Selon la variante que j'essaie, j'obtiens soit des problèmes de durée de vie, soit des incompatibilités de type. Plus précisément, l'argument &self
Ne semble pas être traité comme le bon type.
J'ai vu des exemples avec des arguments de modèle sur impl
ainsi que Add
, mais ils entraînent simplement des erreurs différentes.
J'ai trouvé Comment un opérateur peut-il être surchargé pour différents types RHS et valeurs de retour? mais le code dans la réponse ne fonctionne pas même si je mets un use std::ops::Mul;
En haut.
J'utilise rustc 1.0.0 tous les soirs (ed530d7a3 2015-01-16 22:41:16 +0000)
Je n'accepterai pas "vous n'avez que deux champs, pourquoi utiliser une référence" comme réponse; Et si je voulais une structure à 100 éléments? J'accepterai une réponse qui démontre que même avec une grande structure, je devrais passer par valeur, si tel est le cas (je ne pense pas, cependant.) Je suis intéressé à connaître une bonne règle de base pour la taille de la structure et en passant par valeur vs struct, mais ce n'est pas la question actuelle.
Vous devez implémenter Add
sur &Vector
plutôt que sur Vector
.
impl<'a, 'b> Add<&'b Vector> for &'a Vector {
type Output = Vector;
fn add(self, other: &'b Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
Dans sa définition, Add::add
prend toujours self
par valeur. Mais les références sont des types comme les autres1, afin qu'ils puissent également mettre en œuvre des traits. Lorsqu'un trait est implémenté sur un type de référence, le type de self
est une référence; la référence est passée par valeur. Normalement, le passage par valeur dans Rust implique le transfert de propriété, mais lorsque les références sont passées par valeur, elles sont simplement copiées (ou réorientées/déplacées s'il s'agit d'une référence mutable), et cela ne signifie pas transférer la propriété du référent (car une référence ne possède pas son référent en premier lieu). Compte tenu de tout cela, il est logique pour Add::add
(et de nombreux autres opérateurs) pour prendre self
par valeur: si vous devez vous approprier les opérandes, vous pouvez implémenter Add
sur les structures/énumérations directement, et si vous ne le faites pas , vous pouvez implémenter Add
sur les références.
Ici, self
est de type &'a Vector
, car c'est le type sur lequel nous implémentons Add
.
Notez que j'ai également spécifié le paramètre de type RHS
avec une durée de vie différente pour souligner le fait que les durées de vie des deux paramètres d'entrée ne sont pas liées.
1 En fait, les types de référence sont spéciaux en ce sens que vous pouvez implémenter des traits pour les références aux types définis dans votre caisse (c'est-à-dire si vous êtes autorisé à implémenter un trait pour T
, vous êtes également autorisé à l'implémenter pour &T
). &mut T
et Box<T>
ont le même comportement, mais ce n'est pas vrai en général pour U<T>
où U
n'est pas défini dans la même caisse.
Si vous souhaitez prendre en charge tous les scénarios, vous devez prendre en charge toutes les combinaisons:
In Rust proper, cela a été fait via une macro interne .
Heureusement, il y a une Rust caisse, impl_os , qui propose également une macro pour écrire ce passe-partout pour nous: la caisse propose la impl_op_ex! = macro, qui génère toutes les combinaisons.
Voici leur échantillon:
#[macro_use] extern crate impl_ops;
use std::ops;
impl_op_ex!(+ |a: &DonkeyKong, b: &DonkeyKong| -> i32 { a.bananas + b.bananas });
fn main() {
let total_bananas = &DonkeyKong::new(2) + &DonkeyKong::new(4);
assert_eq!(6, total_bananas);
let total_bananas = &DonkeyKong::new(2) + DonkeyKong::new(4);
assert_eq!(6, total_bananas);
let total_bananas = DonkeyKong::new(2) + &DonkeyKong::new(4);
assert_eq!(6, total_bananas);
let total_bananas = DonkeyKong::new(2) + DonkeyKong::new(4);
assert_eq!(6, total_bananas);
}
Encore mieux, ils ont un impl_op_ex_commutative! qui générera également les opérateurs avec les paramètres inversés si votre opérateur est commutatif.