k.nyao
k.nyao

Reputation: 31

How do I treat generics?

I would like to calculate a factorial using generics, but I get an error in main.

My full code:

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

This error occurs in 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

Upvotes: 2

Views: 92

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65692

The key to the answer is in this note from the compiler:

note: the method new exists but the following trait bounds were not satisfied: Body : std::marker::Sized, Body : Body

You've defined B as a type alias for Body, so the names B and Body both mean the same thing in your program (at least in this module).

When you define a trait, the compiler also defines a type with the same name. However, that type cannot be instantiated directly (unlike a class in C++/C#/Java/etc.). Yet, that's exactly what you're trying to do!

The trait bound Body : std::marker::Sized is not satisfied because Body, being the type defined by the compiler corresponding to the trait of the same name, is an unsized type. Unsized types can only be used in pointers and references (e.g. &Body, Box<Body>, etc.).

The trait bound Body : Body is not satisfied because your trait is not object-safe. It's not object-safe because the method new doesn't have a self parameter (this is what the compiler means in the last note: method `new` has no receiver).

Normally, you would define a struct or an enum and implement the trait for that type, and then use that type when instantiating RecursiveCall. Try replacing type B = Body; with the following:

struct B;

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

Upvotes: 5

Related Questions