2015-06-26 10 views
5

Ich versuche, eine Basiseigenschaft zu erstellen, die andere Operatoreigenschaften (Add, Subtract, Multiply, Divide, etc ...) für mich implementieren wird.Gibt es eine Möglichkeit, ein Merkmal über ein anderes Merkmal zu implementieren?

Dies kann nicht kompiliert werden, es sieht aus wie eine mit Sized ausgestellte, aber auch wenn Measurement auf Sized festgelegt ist, funktioniert es nicht. Ist das überhaupt möglich?

use std::ops::Add; 

#[derive(Copy, Clone, Debug)] 
struct Unit { 
    value: f64, 
} 

impl Unit { 
    fn new(value: f64) -> Unit { 
     Unit { value: value } 
    } 
} 

trait Measurement: Sized { 
    fn get_value(&self) -> f64; 
    fn from_value(value: f64) -> Self; 
} 

impl Measurement for Unit { 
    fn get_value(&self) -> f64 { 
     self.value 
    } 
    fn from_value(value: f64) -> Self { 
     Unit::new(value) 
    } 
} 

// This explicit implementation works 
/* 
impl Add for Unit { 
    type Output = Unit; 

    fn add(self, rhs: Unit) -> Unit { 
     let a = self.get_value(); 
     let b = rhs.get_value(); 
     Unit::from_value(a + b) 
    } 
} 
*/ 

// This trait implementation does not 
impl Add for Measurement { 
    type Output = Self; 

    fn add(self, rhs: Self) -> Self { 
     let a = self.get_value(); 
     let b = rhs.get_value(); 
     Self::from_value(a + b) 
    } 
} 

fn main() { 
    let a = Unit::new(1.5); 
    let b = Unit::new(2.0); 
    let c = a + b; 

    println!("{}", c.get_value()); 
} 

(playground)

error[E0277]: the trait bound `Measurement + 'static: std::marker::Sized` is not satisfied 
    --> src/main.rs:42:6 
    | 
42 | impl Add for Measurement { 
    |  ^^^ `Measurement + 'static` does not have a constant size known at compile-time 
    | 
    = help: the trait `std::marker::Sized` is not implemented for `Measurement + 'static` 

error[E0038]: the trait `Measurement` cannot be made into an object 
    --> src/main.rs:42:6 
    | 
42 | impl Add for Measurement { 
    |  ^^^ the trait `Measurement` cannot be made into an object 
    | 
    = note: the trait cannot require that `Self : Sized` 

error[E0038]: the trait `Measurement` cannot be made into an object 
    --> src/main.rs:43:5 
    | 
43 |  type Output = Self; 
    |  ^^^^^^^^^^^^^^^^^^^ the trait `Measurement` cannot be made into an object 
    | 
    = note: the trait cannot require that `Self : Sized` 

error[E0038]: the trait `Measurement` cannot be made into an object 
    --> src/main.rs:45:5 
    | 
45 |  fn add(self, rhs: Self) -> Self { 
    |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Measurement` cannot be made into an object 
    | 
    = note: the trait cannot require that `Self : Sized` 

Antwort

4

Das Problem ist nicht mit Sized. Die Syntax Sie suchen ist:

impl<T: Measurement> Add for T { ... } 

statt:

impl Add for Measurement { ... } 

Da die rechte Seite des for muss ein Objekt sein, nicht eine Eigenschaft, aber ein Typ-Parameter beschränkt auf Ein Merkmal (dh ein T erforderlich sein Measurement) ist gültig.


Jetzt wird Ihr Code immer noch nicht kompilieren. Sie erhalten folgendes:

error: type parameter T must be used as the type parameter for some local type (e.g. MyStruct<T>); only traits defined in the current crate can be implemented for a type parameter [E0210]

Das Problem hier ist von einer ganz anderen Art. Ich bin mir nicht sicher, ob es mehr mit der Frage zu tun hat, aber ich werde immer noch erklären, was vor sich geht. Wenn Sie ein Impl für Add zu einem beliebigen T schreiben, das Measurement ist, öffnen Sie die Möglichkeit, dass ein Typ Add für sich allein bereits implementiert und Measurement woanders implementieren würde. Stellen Sie sich vor, wenn Sie Measurement auf u8 implementieren möchten (was albern ist aber möglich): Welche Impl sollte Rust für Add wählen? Das Original std impl oder Measurement impl? (in-depth discussion about this issue)

Im Moment verbietet Rust einfach einen Impl, wenn es nicht mindestens 1) dein eigenes Merkmal oder 2) dein eigener Typ ist (wo "eigenes" formal bedeutet, in der Kiste schreibst du dein impl). Deshalb können Sie impl Add for Unit schreiben: weil Sie Unit besitzen.

Die einfachste Lösung wäre, Add aufzugeben und Add unabhängig für jeden Typ zu implementieren, den Sie planen, Unit zu machen. Sagen Sie, Ihre Kiste definiert Inches und Centimeter, jede würde ihre eigene Add Impl haben. Wenn der Code beleidigend ähnlich ist und Sie das Gefühl haben, dass Sie TROCKEN gebrochen haben, nutzen Sie macros. Hier ist how the std crate does it:

macro_rules! add_impl { 
    ($($t:ty)*) => ($(
     #[stable(feature = "rust1", since = "1.0.0")] 
     impl Add for $t { 
      type Output = $t; 

      #[inline] 
      fn add(self, other: $t) -> $t { self + other } 
     } 

     forward_ref_binop! { impl Add, add for $t, $t } 
    )*) 
} 
+0

Dies ist der gleiche Ort, wo ich war gerade bei der Ankunft. Die "T: Measurement" -Syntax wäre so schön, aber ich verstehe. Danke für die Verbindung zu Makros, und wenn ich in der Lage bin, ein Makro zu machen und es über Module zu teilen, die am besten wären. – jocull

+0

Die Frage wurde mit einer funktionierenden Version aktualisiert, die ein grundlegendes Makro zeigt :) Danke! – jocull

0

Sie nicht ein Merkmal für ein Merkmal implementieren, implementieren Sie eine Eigenschaft nur bei den Typen. Sie können jedoch ein Merkmal für einen generischen Typ implementieren, der bestimmte Merkmale (Merkmalsgrenzen) implementiert. Etwas wie folgt aus:

impl<T : Measurement> Add<T> for T { 
    type Output = T; 

    fn add(self, rhs: Self) -> T { 
     let a = self.get_value(); 
     let b = rhs.get_value(); 
     T::from_value(a + b) 
    } 
} 

Leider können Sie diese in Ihre Kiste (seine genannte Kohärenz) definiert für Züge nur tun, damit Sie nicht, dass für die std Add Zug tun können, weil es in der std Kiste definiert ist, nicht in deinem.

Ich denke, Sie müssen einige Makros definieren, um zu tun, was Sie tun möchten.

0

Hier ist eine Arbeitsversion mit Makros, wie vorgeschlagen:

use std::ops::Add; 

#[derive(Copy, Clone, Debug)] 
struct Unit { 
    value: f64, 
} 

impl Unit { 
    fn new(value: f64) -> Unit { 
     Unit { value: value } 
    } 
} 

trait Measurement: Sized { 
    fn get_value(&self) -> f64; 
    fn from_value(value: f64) -> Self; 
} 

impl Measurement for Unit { 
    fn get_value(&self) -> f64 { 
     self.value 
    } 
    fn from_value(value: f64) -> Self { 
     Unit::new(value) 
    } 
} 

macro_rules! add_impl { 
    ($($t:ty)*) => ($(
     impl Add for $t { 
      type Output = $t; 

      fn add(self, other: $t) -> $t { 
       let a = self.get_value(); 
       let b = other.get_value(); 
       let r = a + b; 
       Self::from_value(r) 
      } 
     } 
    )*) 
} 

add_impl! { Unit } 

fn main() { 
    let a = Unit::new(1.5); 
    let b = Unit::new(2.0); 
    let c = a + b; 

    println!("{}", c.get_value()); 
} 
Verwandte Themen