How to Implicitly Pass a Generic Type to an Associated Function

Hello,

Please I have a struct that has a field (called params) with a generic type
e.g

struct Request<T> {
    method: String,
    params: T,
    id: String,
}

And I have an enum called Method which determines the data type for params when I create a new Request.

enum Method {
    Init,
    Register,
}

And these are the types that can be assigned to params:

struct InitRequest;
struct Register;
...

Now, I want to implement an associated function called new that uses Method to determine the type for params. How can I implement Request::new so that the function can be called this way:

let req = Request::new(Method::Init);

Here is the implementation I want, but how do I make rust know the type for params?

impl<T> Request<T> {
    fn new(method: Method) -> Request {
        match method {
            Method::Init => {
                Request {method: String::from("init"),
                    params: InitRequest::new(),
                    id: String::from("xxx"),
                }
            },
            Method::Register => {
                Request {method: String::from("register"),
                    params: Register::new(),
                    id: String::from("xxx"),
                }
            }
        }
    }
}

How do I make Rust determine the type for params without passing it as an argument to Request::new(Method)?

I could have done something like this:

impl<T> Request<T> {
    fn new(params: T) -> Request<T> {
                Request {
                   method: String::from("init"),
                    params: params,
                    id: String::from("xxx"),
                }
}
...

But Id on't want to pass the generic type T as an argument to the associated function new.

Reference code: Rust Playground

Thanks

You can't use generics in this way. Consider using an enum to store the value instead?

1 Like

Thanks Alice for the feedback. Kindly expantiate what you mean.

You could use:

enum RequestParams {
    InitParams(InitParams),
    Register(RegisterParams),
}

If it's an HTTP request, you can probably just use strings for params, e.g. params: Vec<String> (or HashMap, etc.)

If you really need structs, then define

trait Method {
    type Params;
}
struct InitMethod;
impl Method for InitMethod {
    type Params = MethodParams;
}

fn new<M: Method>(method: M) -> Request<Method::Params>
1 Like

Thanks Kornel. I will try the traits option because the params field takes several complex structs.

The enum approach worked. Thanks Kornel and Alice.