This trait cannot be made into an object because associated function `new` has no `self` parameter

I'm writing a function that does some processing on various data. I want to be able to take various action (for example, to have a dry run mode).

I was thinking about having a trait that takes the proper action once. Something like this:

trait DoStuff {
    fn do_it(n: u32) -> u32;
}

struct DoItDry;

impl DoStuff for DoItDry {
    fn do_it(n: u32) -> u32 {
        println!("Got {}", n);
        n
    }
}

fn process(nums: &[u32], doer: &dyn DoStuff) {
    for n in nums {
        if *n > 1 && *n < 5 {
            doer.do_it(*n);
        }
    }
}

However this fails because: error[E0038]: the trait DoStuff cannot be made into an object, with a few more details:

  --> src\main.rs:2:8
   |
1  | trait DoStuff {
   |       ------- this trait cannot be made into an object...
2  |     fn do_it(n: u32) -> u32;// where Self: Sized;
   |        ^^^^^ ...because associated function `do_it` has no `self` parameter
help: consider turning `do_it` into a method by giving it a `&self` argument
   |
2  |     fn do_it(&self, n: u32) -> u32;// where Self: Sized;
   |              ^^^^^^
help: alternatively, consider constraining `do_it` so it does not apply to trait objects
   |
2  |     fn do_it(n: u32) -> u32 where Self: Sized;// where Self: Sized;

I don't really need a self in this case, right? So I tried the second option:

trait DoStuff {
    fn do_it(n: u32) -> u32 where Self: Sized;
}

struct DoItDry;

impl DoStuff for DoItDry {
    fn do_it(n: u32) -> u32 where Self: Sized {
        println!("Got {}", n);
        n
    }
}

fn process(nums: &[u32], doer: &dyn DoStuff) {
    for n in nums {
        if *n > 1 && *n < 5 {
            doer.do_it(*n);
        }
    }
}

This still does not compile:

error[E0599]: no method named `do_it` found for reference `&dyn DoStuff` in the current scope
  --> src\main.rs:17:18
   |
17 |             doer.do_it(*n);
   |             -----^^^^^----
   |             |    |
   |             |    this is an associated function, not a method
   |             help: disambiguate the associated function for the candidate: `DoStuff::do_it(&doer, *n)`

I don't really understand why this happens, so I tried the first suggestion:

trait DoStuff {
    fn do_it(&self, n: u32) -> u32;
}

struct DoItDry;

impl DoStuff for DoItDry {
    fn do_it(&self, n: u32) -> u32 {
        println!("Got {}", n);
        n
    }
}

fn process(nums: &[u32], doer: &dyn DoStuff) {
    for n in nums {
        if *n > 1 && *n < 5 {
            doer.do_it(*n);
        }
    }
}

fn main() {
    let x: [u32; 3] = [1, 2, 3];
    let d: DoItDry = DoItDry{};

    process(&x, &d);
}

Now this works, but what if I want to actually have a new function for the trait (which I do in my actual use case)? Like this:

trait DoStuff {
    fn new() -> Self;
    fn do_it(&self, n: u32) -> u32;
}

Now I'm back to square one. How can I do this?

A type's freestanding functions which don't take self in any form require knowledge of the type.

For example,

struct Foo;

impl Foo {
    fn bar() {}
}

In this instance, I can only call bar by saying Foo::bar().

With trait objects, the concrete type you have is dyn Trait, whose implementation of the trait Trait defers to metadata stored with the pointer to self. I think you can see where I'm going with this: you can't logically call <dyn Trait>::standalone_method() since it wouldn't have access to that metadata in self's pointer.

Instead, I'd recommend using generics:

trait DoStuff {
    fn do_it(n: u32) -> u32;
}

struct DoItDry;

impl DoStuff for DoItDry {
    fn do_it(n: u32) -> u32 {
        println!("Got {}", n);
        n
    }
}

fn process<T: DoStuff>(nums: &[u32], _doer: &T) {
    for n in nums {
        if *n > 1 && *n < 5 {
            T::do_it(*n);
        }
    }
}

Notice how here we definitely have a concrete type to talk about: T.

1 Like

This makes a lot of sense, thank you. Unfortunately I can't use generics, as I want to keep some internal state for some DoStuff implementations. But I can still have my new if I don't put it in the DoStuff trait, so everything's good. Like this:

trait DoStuff {
    fn do_it(&self, n: u32) -> u32;
}

struct DoItDry;

impl DoStuff for DoItDry {
    fn do_it(&self, n: u32) -> u32 {
        println!("Got {}", n);
        n
    }
}

impl DoItDry {
    fn new() -> Self {
        Self{}
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.