2015-01-17 10 views
18

Ich habe eine zwei Elemente Vector Struktur und ich möchte den + Operator überladen.Wie implementiere ich das Add-Merkmal für eine Referenz auf eine Struktur?

Ich machte alle meine Funktionen und Methoden nehmen Referenzen, anstatt Werte, und ich möchte die +-Operator auf die gleiche Weise arbeiten.

Je nachdem, welche Variante ich versuche, bekomme ich entweder Lebensdauerprobleme oder Typenkonflikte. Insbesondere scheint das &self Argument nicht als der richtige Typ behandelt zu werden.

Ich habe Beispiele mit Vorlagenargumenten auf impl sowie Add gesehen, aber sie führen nur zu unterschiedlichen Fehlern.

Ich fand How can an operator be overloaded for different RHS types and return values? aber der Code in der Antwort funktioniert nicht, auch wenn ich einen use std::ops::Mul; an der Spitze setzen.

Ich verwende rustc 1.0.0-nächtliche (ed530d7a3 2015-01-16 22:41:16 +0000)

Ich werde nicht akzeptieren „Sie nur zwei Felder haben, warum eine Referenz verwenden“, wie eine Antwort; Was, wenn ich eine Struktur mit 100 Elementen wollte? Ich werde eine Antwort akzeptieren, die zeigt, dass selbst bei einer großen Struktur der Wert überschritten werden sollte, wenn das der Fall ist (ich glaube jedoch nicht.) Ich bin daran interessiert, eine gute Faustregel für die Strukturgröße zu kennen und Übergabe an Wert gegen Struktur, aber das ist nicht die aktuelle Frage.

+0

"Was ist, wenn ich eine Struktur mit 100 Elementen wollte" - Rust verwendet Optimierungen wie RVO, die automatisch eine Referenz verwenden, wenn es angebracht ist und die bessere Wahl. – Shepmaster

+0

@Shempmaster: RVO wirkt sich nur auf den Rückgabewert aus, den ich als Wert zurückgebe. Können Sie auf eine Dokumentation verweisen, die zeigt, dass Merkmale für große Strukturen wertmäßig implementiert werden sollten? –

+1

Die beste Dokumentation, die ich kenne, wäre das [Buch Kapitel über zurückkehrende Zeiger] (http://doc.rust-lang.org/book/pointers.html#returning-pointers). Allerdings habe ich [ein Beispiel für das Hinzufügen einer großen Struktur erstellt] (http://is.gd/25ITa7) und überprüft die generierte LLVM (leicht gereinigt): '(% struct.Big * sret,% struct.Big *,% struct.Big *) '. Ich behaupte nicht, ein LLVM-Experte zu sein, aber das sieht so aus, als würde es automatisch als Referenz genommen und zurückgegeben. – Shepmaster

Antwort

28

Sie müssen Add unter &Vector anstelle von Vector implementieren.

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, 
     } 
    } 
} 

In seiner Definition Add::add nimmt immer self von Wert. Aber Referenzen sind Typen wie alle anderen , so dass sie auch Merkmale implementieren können. Wenn eine Eigenschaft für einen Referenztyp implementiert ist, ist der Typ self eine Referenz. Die Referenz wird als Wert übergeben. Normalerweise bedeutet das Übergeben nach Wert in Rust die Übertragung von Eigentumsrechten, aber wenn Referenzen nach Wert übergeben werden, werden sie einfach kopiert (oder rebororiert/verschoben, wenn es sich um eine änderbare Referenz handelt), und das Eigentum des Referenten wird nicht übertragen (weil eine Referenz besitzt seinen Referenten nicht in erster Linie). Unter Berücksichtigung all dies macht es Sinn für Add::add (und viele andere Operatoren), self nach Wert zu nehmen: Wenn Sie Besitz der Operanden übernehmen müssen, können Sie Add direkt auf structs/enums implementieren, und wenn nicht, können Sie Implementieren Sie Add auf Referenzen.

Hier ist self vom Typ &'a Vector, denn das ist der Typ, den wir implementieren Add auf.

Beachten Sie, dass ich auch den Typparameter RHS mit einer anderen Lebensdauer angegeben habe, um die Tatsache hervorzuheben, dass die Lebensdauern der beiden Eingabeparameter nicht zusammenhängen.


Eigentlich sind Referenztypen speziell, da Sie Merkmale für Verweise auf Typen definiert in Ihrer Kiste implementieren kann (dh, wenn Sie erlaubt sind ein Merkmal für T zu implementieren, dann sind Sie auch erlaubt um es für &T zu implementieren). &mut T und Box<T> haben das gleiche Verhalten, aber das gilt nicht allgemein für U<T> wo U ist nicht in der gleichen Kiste definiert.

+3

" Add :: add nimmt immer self nach Wert. Hier, self ist vom Typ & 'ein Vektor, weil das der Typ ist, den wir implementieren Add on. "Das ist die Schlüsselinformation, dass der Typ des Selbst sich ändert, abhängig davon, ob das Merkmal eine Referenz ist oder nicht. Danke! –

+2

Wow dass dies die richtige Antwort ist, und doch ist es das. Das alles fühlt sich ziemlich kontraintuitiv an.Das kann man Add auf zwei verschiedene Arten definieren, je nachdem, ob es eine Referenz ist oder nicht wie ein Rezept für Ärger. – Squirrel