2015-01-09 15 views
7

kompilacji następujący kod rdzy, która używa operatora przeciążeniaOperator przeciążenie wynikami wartość użytkową przeniósł wartości

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

Wyniki błędy kompilatora powodu posiadania 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; 
        ^

Uzasadnieniem tego zostało wyjaśnione here i doprowadziło do RFC, które nie zostało zaakceptowane (częściowo ze względu na przyczyny powyższego przykładu). Jednak później następujące RFC nadal wprowadził sygnatury typu wartości bezwzględnej dla operatorów.

Chociaż rozumiem uzasadnienie decyzji. Z powodu braku doświadczenia w rdzy, nie jestem pewien, jaki byłby "właściwy" sposób, aby powyższy kod mógł działać (a) jeśli nie chcę kopiować lub (b) jak uczynić strukturę kopiowalną?

Odpowiedz

4

Jeśli nie chcesz kopiować, to jeśli chodzi o moje zrozumienie dla początkujących, musisz wdrożyć Add w odniesieniu do odniesień do Point.

To byłaby wspierana przez RFC:

Na szczęście, nie ma strat w wyrazistości, gdyż zawsze można wdrożyć na cechę typów referencyjnych. Jednak w przypadku typów, które muszą być brane przez odniesienie, występuje niewielka utrata ergonomii, ponieważ może być konieczne wyraźne pożyczenie argumentów operacji za pomocą &. Plusem jest to, że semantyka własności staje się wyraźniejsza: bardziej przypominają one zwykłe argumenty funkcji.

I rzeczywiście wydaje się działać:

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)

Aby Point copyable zamiast, po prostu zastąpić #[derive(Show)] z #[derive(Show,Copy)]. Takie struktury były domyślnie kopiowalne, ale to changed.

+1

Problem polega na tym, że 'let pp = & p + & p + & p' nie działa. – SirVer

+0

@SirVer tak, musisz napisać coś w stylu 'let pp = & (& p + & p) + & p'. Myślę, że praktyczną rzeczą byłoby stworzenie kilku implementacji, jak sugeruje Vladimir Matveev (lub po prostu czerpanie z "Copy" i koniec z nim). –

4

Jeśli twoja struktura nie może być skopiowana (np. Ma implementację Drop, sama lub dla jednego z jej pól), może mieć sens utworzenie kilku implementacji: wartość + wartość, wartość + odniesienie, odniesienie + wartość i odniesienie + odniesienie. Pierwsze trzy mogą ponownie wykorzystać pamięć jednego z operandów, a ostatni może sklonować jeden z operandów, a następnie przekazać tylko istniejące implementacje. W ten sposób użytkownik Twojej biblioteki może łatwo zdecydować, czy chce ponownie wykorzystać istniejące wartości do optymalizacji, czy nie.

W ten sposób np. Obsługiwane są typy BigInt lub Complex.

Jednak Twoja Point może być po prostu Copy, ponieważ jest tanie w kopiowaniu.

+0

Vladimir, dziękuję za odpowiedź. Co się stanie, jeśli mój typ jest łatwy, ale drogi do skopiowania, np. Typu 'Matrix1000x1000'?Czy kompilator jest wystarczająco inteligentny, aby uniknąć kopiowania w połączonych operacjach, czy też muszę pisać trywialnie do 4 wspomnianych implementacji? – SirVer

+1

Nie jestem pewien, czy rozumiem twoje pytanie. Jeśli twój typ jest drogi do skopiowania, nie rób tego "Kopiuj" i nie wprowadzaj cech operacyjnych dla czterech wariantów (self + self, i self + self, self + i self, self + i self). Jeśli twój typ jest tak duży, będziesz musiał umieścić dane na kupce, więc semantyka sprawi, że skopiowana zostanie tylko sama struktura. –

Powiązane problemy