2015-01-17 8 views
18

Ho creato una struttura a due elementi Vector e desidero sovraccaricare l'operatore +.Come si implementa il tratto Aggiungi per un riferimento a una struttura?

Ho fatto in modo che tutte le mie funzioni e metodi prendessero riferimenti, piuttosto che valori, e voglio che l'operatore + funzioni allo stesso modo.

impl Add for Vector { 
    fn add(&self, other: &Vector) -> Vector { 
     Vector { 
      x: self.x + other.x, 
      y: self.y + other.y, 
     } 
    } 
} 

A seconda della variante che cerco, o ottengo problemi di durata o tipo mancate corrispondenze. In particolare, l'argomento &self sembra non essere trattato come il tipo giusto.

Ho visto esempi con argomenti modello su impl e Add, ma hanno solo errori diversi.

Ho trovato How can an operator be overloaded for different RHS types and return values? ma il codice nella risposta non funziona anche se inserisco uno use std::ops::Mul; nella parte superiore.

Sto usando rustc 1.0.0-serale (ed530d7a3 2015-01-16 22:41:16 +0000)

Non accetterò "hai solo due campi, perché usare un punto di riferimento", come una domanda; e se volessi una struct di 100 elementi? Accetterò una risposta che dimostri che anche con una grande struttura dovrei passare per valore, se questo è il caso (non penso che lo sia, però). Sono interessato a conoscere una buona regola empirica per le dimensioni della struttura e passando per value vs struct, ma questa non è la domanda corrente.

+0

"e se volessi una struct 100 elementi" - Rust utilizza ottimizzazioni come RVO che utilizzerà automaticamente un riferimento quando appropriato e la scelta migliore. – Shepmaster

+0

@Shepmaster: RVO influenzerà solo il valore di ritorno, che sto restituendo in base al valore. Puoi indicare tutta la documentazione che mostra che i tratti per le grandi strutture dovrebbero essere implementati in base al valore? –

+1

La migliore documentazione che conosco sarebbe il [capitolo del libro sui puntatori di ritorno] (http://doc.rust-lang.org/book/pointers.html#returning-pointers). Tuttavia, ho [creato un esempio di aggiunta di una struttura grande] (http://is.gd/25ITa7) e controllato il LLVM generato (leggermente pulito): '(% struct.Big * sret,% struct.Big *,% struct.Big *) '. Non pretendo di essere un esperto di LLVM, ma sembra che stia prendendo e tornando automaticamente per riferimento. – Shepmaster

risposta

28

è necessario implementare Add su &Vector piuttosto che su 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, 
     } 
    } 
} 

Nella sua definizione, Add::add prende sempre self per valore. Ma i riferimenti sono tipi come tutti gli altri , quindi possono implementare anche i tratti. Quando un tratto è implementato su un tipo di riferimento, il tipo di self è un riferimento; il riferimento è passato per valore. Normalmente, passare per valore in Rust implica il trasferimento della proprietà, ma quando i riferimenti vengono passati per valore, vengono semplicemente copiati (o rinviati/spostati se si tratta di un riferimento mutabile) e ciò non trasferisce la proprietà del referente (perché un riferimento non possiede il suo referente in primo luogo). Considerando tutto ciò, è logico che Add::add (e molti altri operatori) prenda il valore self in base al valore: se è necessario assumere la proprietà degli operandi, è possibile implementare direttamente Add in structs/enum e, in caso contrario, è possibile attuare Add sui riferimenti.

Qui, self è di tipo &'a Vector, perché questo è il tipo che stiamo implementando Add su.

Nota che ho anche specificato il parametro di tipo RHS con una durata diversa per sottolineare il fatto che le durate dei due parametri di input non sono correlate.


In realtà, tipi di riferimento sono speciali in quanto è possibile implementare i tratti per i riferimenti a tipi definiti nel vostro cassa (vale a dire se si è permesso di implementare un tratto per T, allora sei anche permesso per implementarlo per &T). &mut T e Box<T> hanno lo stesso comportamento, ma non è vero in generale per U<T> dove U non è definito nella stessa cassa.

+3

"Aggiungi :: aggiungi sempre prende valore per valore. è di tipo & 'a Vector, perché questo è il tipo che stiamo implementando Aggiungi su. "Questa è l'informazione chiave, che il tipo di auto cambia a seconda che il tratto sia o meno un riferimento. Grazie! –

+2

Wow. che questa è la risposta giusta, eppure lo è. Tutto ciò sembra abbastanza contro-intuitivo, che puoi definire Aggiungi in due modi diversi a seconda che si tratti di un riferimento o meno come una ricetta per problemi. – Squirrel

Problemi correlati