2014-10-22 3 views
5

Próbuję zaimplementować szablony ekspresji w stylu C++ w Rust przy użyciu cech i przeciążenia operatora. Utknąłem próbując przeciążać "+" i "*" dla każdej struktury szablonu wyrażenia. Kompilator narzeka implementacji Add i Mul cecha:Przeciążanie operatora dla wszystkich struktur z cechą w Rust

error: cannot provide an extension implementation where both trait and type are not defined in this crate

Błąd miałoby sensu, jeśli typ starałem się wdrożyć cechę dla był constructible bez mojej skrzyni, ale typ jest rodzajowy, które muszą wdrożyć HasValue cecha, którą zdefiniowałem.

Oto kod:

trait HasValue { 
    fn get_value(&self) -> int; 
} 

// Val 

struct Val { 
    value: int 
} 

impl HasValue for Val { 
    fn get_value(&self) -> int { 
    self.value 
    } 
} 


// Add 

struct AddOp<T1: HasValue + Copy, T2: HasValue + Copy> { 
    lhs: T1, 
    rhs: T2 
} 

impl<T1: HasValue + Copy, T2: HasValue + Copy> HasValue for AddOp<T1, T2> { 
    fn get_value(&self) -> int { 
    self.lhs.get_value() + self.rhs.get_value() 
    } 
} 

impl<T: HasValue + Copy, O: HasValue + Copy> Add<O, AddOp<T, O>> for T { 
    fn add(&self, other : &O) -> AddOp<T, O> { 
    AddOp { lhs: *self, rhs: *other } 
    } 
} 


// Mul 

struct MulOp<T1: HasValue + Copy, T2: HasValue + Copy> { 
    lhs: T1, 
    rhs: T2 
} 

impl<T1: HasValue + Copy, T2: HasValue + Copy> HasValue for MulOp<T1, T2> { 
    fn get_value(&self) -> int { 
    self.lhs.get_value() * self.rhs.get_value() 
    } 
} 

impl<T: HasValue + Copy, O: HasValue + Copy> Mul<O, MulOp<T, O>> for T { 
    fn mul(&self, other : &O) -> MulOp<T, O> { 
    MulOp { lhs: *self, rhs: *other } 
    } 
} 


fn main() { 
    let a = Val { value: 1 }; 
    let b = Val { value: 2 }; 
    let c = Val { value: 2 }; 

    let e = ((a + b) * c).get_value(); 

    print!("{}", e); 
} 

Myśli?

Odpowiedz

2

Próbując zdefiniować cechę Add dla was niestandardowych typów, robisz to:

impl<T: HasValue + Copy, O: HasValue + Copy> Add<O, AddOp<T, O>> for T { 
    fn add(&self, other : &O) -> AddOp<T, O> { 
    AddOp { lhs: *self, rhs: *other } 
    } 
} 

Ale T: HasValue + Copy pasuje do każdego typu wykonawczego cechę HasValue, a tego typu nie mogą być definiowane w skrzyni (na przykład, jeśli implementujesz HasValue dla). Ponieważ Add również nie jest zdefiniowany w twojej skrzynce, Rust narzeka: na przykład definiując HasValue dla int, możesz również ponownie zdefiniować Add dla int!

Moja sugestia polegałaby na zawinięciu całej operacji i wartości w postaci ogólnej, a następnie zaimplementowaniu Add i Mul. W ten sposób implementujesz Add i Mul tylko dla prostego typu zdefiniowanego w twojej skrzynce, a kompilator jest zadowolony.

Coś takiego:

struct Calculus<T: HasValue + Copy> { 
    calc: T 
} 

impl<T: HasValue + Copy> HasValue for Calculus<T> { 
    fn get_value(&self) -> int { 
     self.calc.get_value() 
    } 
} 

impl<T, O> Add<Calculus<O>, Calculus<AddOp<T, O>>> for Calculus<T> 
    where T: HasValue + Copy, O: HasValue + Copy { 
    fn add(&self, other : &Calculus<O>) -> Calculus<AddOp<T, O>> { 
    Calculus { calc: AddOp { lhs: self.calc, rhs: other.calc } } 
    } 
} 

impl<T, O> Mul<Calculus<O>, Calculus<MulOp<T, O>>> for Calculus<T> 
where T: HasValue + Copy, O: HasValue + Copy { 
    fn mul(&self, other : &Calculus<O>) -> Calculus<MulOp<T, O>> { 
    Calculus { calc: MulOp { lhs: self.calc, rhs: other.calc } } 
    } 
} 

Następnie można po prostu dodać schludny new() metodę typu Val:

impl Val { 
    fn new(n: int) -> Calculus<Val> { 
    Calculus { calc: Val { value: n } } 
    } 
} 

i wykorzystać całą sprawę tak:

fn main() { 
    let a = Val::new(1); 
    let b = Val::new(2); 
    let c = Val::new(3); 

    let e = ((a + b) * c).get_value(); 

    print!("{}", e); 
} 
+0

Innym rozwiązaniem byłoby stworzenie nowej cechy i mieć cechę zaimplementować zarówno "HasValue" i "Copy". Następnie można wprowadzić ogólną implementację 'Dodaj' dla tej cechy. –

+0

@ MatthieuM. Jesteś pewny ? Nie widzę, jak rozwiązuje problem, ponieważ nadal można zaimplementować tę nową cechę dla już istniejącego typu. – Levans

+0

Teoretycznie jest to poprawne, ponieważ podczas implementowania super-cechy dla typu, musisz wiedzieć, czy ten typ implementuje już jakąkolwiek subtrait (i tym samym może zdecydować, czy zignorować domyślną implementację supertytu, czy podnieść błąd). W praktyce nie wiem, czy Rust pozwala na to jeszcze, i jeśli robi to, którą semantykę wybrał (ignorowanie lub błąd). –