2017-02-21 2 views
2

Je voudrais calculer un factoriel en utilisant des génériques, mais j'obtiens une erreur dans main.Comment traiter les génériques?

Mon code complet:

pub trait Body { 
    fn new() -> Self; 

    fn fact(&self, x: usize) -> usize { 
     match x { 
      1 => 1, 
      _ => x * self.fact(x - 1), 
     } 
    } 
} 

#[derive(Clone, Debug)] 
pub struct RecursiveCall<T: Body> { 
    level: usize, 
    indicator: String, 
    n_repeat: usize, 
    body: T, 
} 

impl<T> RecursiveCall<T> 
    where T: Body 
{ 
    fn new(n_repeat: usize) -> RecursiveCall<T> { 
     RecursiveCall { 
      level: 0, 
      indicator: "- ".to_string(), 
      n_repeat: n_repeat, 
      body: <T as Body>::new(), 
     } 
    } 

    fn pre_trace(&self, fname: &str, arg: &usize) { 
     let args: String = arg.to_string(); 
     println!("{}", 
       (vec![self.indicator.as_str(); self.level]).join("") + 
       self.level.to_string().as_str() + ":" + fname + "(" + 
       args.as_str() + ")"); 
    } 

    fn post_trace(&self, fname: &str, arg: &usize, ret: &usize) { 
     println!("{}", 
       (vec![self.indicator.as_str(); self.level]).join("") + 
       self.level.to_string().as_str() + ":" + fname + "=" + 
       ret.to_string().as_str()); 
    } 

    fn print_trace(&mut self) { 
     &self.pre_trace("fact", &self.n_repeat); 
     self.level += 1; 
     let ret = &self.body.fact(self.n_repeat); 
     self.level -= 1; 
     &self.post_trace("fact", &self.n_repeat, ret); 

     println!("Difference={}", &ret.to_string().as_str()); 
    } 
} 

type B = Body; 
fn main() { 
    let t = RecursiveCall::<B>::new(); 
} 

Cette erreur se produit dans main():

error: no associated item named `new` found for type `RecursiveCall<Body + 'static>` in the current scope 
    --> src/main.rs:61:13 
    | 
61 |  let t = RecursiveCall::<B>::new(); 
    |    ^^^^^^^^^^^^^^^^^^^^^^^ 
    | 
    = note: the method `new` exists but the following trait bounds were not satisfied: `Body : std::marker::Sized`, `Body : Body` 
    = help: items from traits can only be used if the trait is implemented and in scope; the following traits define an item `new`, perhaps you need to implement one of them: 
    = help: candidate #1: `Body` 
    = help: candidate #2: `std::sys_common::thread_info::NewThread` 
    = help: candidate #3: `std::iter::ZipImpl` 

error[E0277]: the trait bound `Body + 'static: std::marker::Sized` is not satisfied 
    --> src/main.rs:61:13 
    | 
61 |  let t = RecursiveCall::<B>::new(); 
    |    ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Sized` is not implemented for `Body + 'static` 
    | 
    = note: `Body + 'static` does not have a constant size known at compile-time 
    = note: required by `RecursiveCall` 

error[E0277]: the trait bound `Body + 'static: Body` is not satisfied 
    --> src/main.rs:61:13 
    | 
61 |  let t = RecursiveCall::<B>::new(); 
    |    ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Body` is not implemented for `Body + 'static` 
    | 
    = note: required by `RecursiveCall` 

error[E0038]: the trait `Body` cannot be made into an object 
    --> src/main.rs:61:13 
    | 
61 |  let t = RecursiveCall::<B>::new(); 
    |    ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Body` cannot be made into an object 
    | 
    = note: method `new` has no receiver 

Répondre

5

La clé de la réponse est dans cette note du compilateur:

Note: la la méthode new existe mais les limites suivantes n'ont pas été satisfaites: Body : std::marker::Sized, Body : Body

Vous avez défini B comme un alias de type pour Body, de sorte que les noms B et Body les deux veulent dire la même chose dans votre programme (au moins dans ce module).

Lorsque vous définissez un trait, le compilateur définit également un type avec le même nom. Cependant, ce type ne peut pas être instancié directement (contrairement à une classe en C++/C#/Java/etc.). Pourtant, c'est exactement ce que vous essayez de faire!

Le caractère lié Body : std::marker::Sized n'est pas satisfait car Body, étant le type défini par le compilateur correspondant à l'attribut du même nom, est un type non dimensionné. Les types non notés ne peuvent être utilisés que dans les pointeurs et les références (par exemple &Body, Box<Body>, etc.).

Le caractère lié Body : Body n'est pas satisfait car votre trait n'est pas object-safe. Ce n'est pas un objet-sûr parce que la méthode new n'a pas de paramètre self (c'est ce que le compilateur signifie dans la dernière note: method `new` has no receiver).

Normalement, vous devez définir une structure ou une énumération et implémenter l'attribut pour ce type, puis utiliser ce type lors de l'instanciation RecursiveCall. Essayez de remplacer type B = Body; par ce qui suit:

struct B; 

impl Body for B { 
    fn new() -> B { 
     B 
    } 
} 
+0

J'ai résolu le problème avec votre aide! Merci pour votre gentillesse. –