2015-01-09 28 views
7

Compilare il seguente codice di ruggine che utilizza l'overloading degli operatorioverloading degli operatori per i risultati di valore d'uso di valore spostato

use std::ops::{Add}; 

#[derive(Show)] 
struct Point { 
    x: int, 
    y: int 
} 

impl Add for Point { 
    type Output = Point; 

    fn add(self, other: Point) -> Point { 
     Point {x: self.x + other.x, y: self.y + other.y} 
    } 
} 

fn main() { 
    let p: Point = Point {x: 1, y: 0}; 
    let pp = p + p; 
} 

Risultati in errori di compilazione dovuti alla proprietà di p:

<anon>:21:18: 21:19 error: use of moved value: `p` 
<anon>:21  let pp = p + p; 
         ^
<anon>:21:14: 21:15 note: `p` moved here because it has type `Point`, which is non-copyable 
<anon>:21  let pp = p + p; 
        ^

La logica dietro di esso è spiegato here e ha portato a uno RFC che non è stato accettato (parte del motivo dell'esempio sopra riportato). Tuttavia, in seguito il seguente RFC ha ancora introdotto le firme del tipo di valore per gli operatori.

Mentre capisco la logica alla base della decisione. A causa della mia mancanza di esperienza nella ruggine, non sono sicuro di quale sia il modo "corretto" per consentire il funzionamento del suddetto codice (a) se non voglio copiare o (b) come rendere la struttura copiabile?

risposta

4

Se non si desidera copiare, per quanto riguarda la comprensione dei nuovi utenti, è necessario implementare Add sui riferimenti a Point.

questo sarebbe supportato dal RFC:

Fortunatamente, non v'è alcuna perdita di espressività, perché si può sempre implementare il tratto sui tipi di riferimento. Tuttavia, per i tipi che devono essere presi come riferimento, vi è una leggera perdita di ergonomia poiché potrebbe essere necessario prendere in prestito esplicitamente gli operandi con &. Il lato positivo è che la semantica della proprietà diventa più chiara: assomigliano più strettamente ai normali argomenti delle funzioni.

E in effetti sembra funzionare:

use std::ops::{Add}; 

#[derive(Show)] 
struct Point { 
    x: i32, 
    y: i32 
} 

impl<'a> Add for &'a Point { 
    type Output = Point; 

    fn add(self, other: &'a Point) -> Point { //' 
     Point {x: self.x + other.x, y: self.y + other.y} 
    } 
} 

fn main() { 
    let p: Point = Point {x: 1, y: 0}; 
    let pp = &p + &p; 
    println!("{:?}", pp); 
} 

(playpen)

Per rendere Point copiabile invece, basta sostituire #[derive(Show)] con #[derive(Show,Copy)]. Tali strutture erano solitamente copiabili, ma era changed.

+1

Il problema con questo è che "let pp = & p + & p + & p' non funziona. – SirVer

+0

@SirVer si, dovresti scrivere qualcosa come 'let pp = & (& p + & p) + & p'. Immagino che la cosa pratica da fare sarebbe creare diverse implementazioni, come suggerisce la risposta di Vladimir Matveev (o semplicemente derivare "Copia" e farla finita). –

4

Se la struttura non può essere copiata (ad esempio, ha l'implementazione Drop, stessa o per uno dei suoi campi), allora può avere senso creare diverse implementazioni: valore + valore, valore + riferimento, riferimento + valore e riferimento + riferimento. I primi tre possono riutilizzare la memorizzazione di uno degli operandi e l'ultimo può clonare uno degli operandi e quindi delegare semplicemente alle implementazioni già esistenti. In questo modo l'utente della tua biblioteca può facilmente decidere se riutilizzare o meno i valori esistenti per l'ottimizzazione.

In effetti, è così ad es. Sono gestiti i tipi BigInt o Complex.

Il tuo Point, tuttavia, può essere effettuato solo Copy poiché è economico copiarlo.

+0

Vladimir, grazie per la tua risposta. Cosa succede se il mio tipo è facile, ma costoso da copiare, ad esempio un tipo 'Matrix1000x1000'?Il compilatore è abbastanza intelligente da evitare di copiare in operazioni concatenate o devo scrivere in modo superficiale le 4 implementazioni che hai menzionato? – SirVer

+1

Non sono sicuro di aver capito la tua domanda. Se il tuo tipo è costoso da copiare, non renderlo 'Copia' e implementa i tratti operativi per quattro varianti (auto + sé, & auto + sé, auto + & sé, e auto + e auto). Se il tuo tipo è così grande, dovrai comunque inserire i dati nell'heap, quindi spostare la semantica farà in modo che solo la struttura stessa venga copiata. –