1
votes

I'm new to Rust and am having trouble working with traits and generics. I started by defining a trait to do some work for me, then defined a generic struct that uses it as a type parameter. Now I realized that in the original trait, I actually want to use the struct I defined, so I'm in a kind of loop. I'm not sure how to get out of it, and wondering if what I want is even possible via generics, or maybe I'll need to use boxing or some other method? Minimal example below.

This is what I started with initially:

trait Worker {
  fn work() -> WorkResult;
}

struct WorkResult {
  result: u64;
}

Next I realized I want to keep a worker on the result so I can lazily generate the the value and use various worker implementations.

trait Worker {
  fn work() -> WorkResult;
}

struct WorkResult<T> where T: Worker {
  result: u64;
  worker: T;
}

This is where stuff started getting weird in the Worker trait. WorkResult now needs a type parameter, but it's unknown in the trait. I tried a bunch of different methods, but can't get the compiler to like it.

Attempt:

trait Worker<T> where T: Worker {
  fn work() -> WorkResult<T>;
}

struct WorkResult<T> where T: Worker {
  result: u64;
  worker: T;
}

That didn't work at all. Compiler doesn't even recognize the parameter:

error[E0107]: wrong number of type arguments: expected 1, found 0

I can't plug in any concrete type here (and I don't think I should have to). Is there any way to accomplish what I want here?

1
I'm curious if you've really intended to make work an associated function and not a method. It should be called without an instance of Worker? But why do you then need this instance inside the WorkResult?Cerberus

1 Answers

0
votes

The problem here is that the Worker trait needs a type parameter even when used as a bound, so you would need to write where T: Worker<U>.

Unfortunately, in the case of the trait definition, this would get recursive. For example, if you did:

trait Worker<T> 
    where T: Worker<U>,
{...}

You would then need to add a second generic parameter to worker, which would necessitate a third, and so on ad infinitum. The other possibility, where T: Worker<T> technically compiles, but it makes the trait impossible (or at the very least, beyond my ability) to successfully implement.

One potential solution would be to move the type parameter to the work function, like so:

trait Worker {
    fn work<T: Worker>() -> WorkResult<T>;
}

struct WorkResult<T: Worker> {
    result: u64,
    worker: T,
}

(Note that struct WorkResult<T: Worker> is just shorthand for struct WorkResult<T> where T: Worker)