Type return on generic function

I'm trying to understand why the compiler can't infer which type should be returned, and why I have to explicitly define it. It's not that that's a big problem, just a little cumbersome and I'm certain it's that I'm doing something wrong.
I've got a type

Struct Plan {
    url: String
}

I've got a trait that I've defined

#[async_trait]
pub trait FromPlace {
    async fn get<T>(client: &reqwest::Client) -> Vec<T> where T: FromWsm  + for<'a> Deserialize<'a> {
        client.get(&T::url()).send().await.unwrap().json::<Vec<T>>().await.unwrap()
    }
    fn url() -> String;
}

and my impl

impl FromPlace for Plan {
    fn url() -> String {
        "http://google.com".to_string()
    }
}

but, when I do

let mut plan = Plan::get(&client).await;

The compiler tells me that the type of plan can't be known at this point and I have to manually specificy it.

This resolves the issue

let mut plans: Vec<Plan> = Plan::get(&client).await

but I'd like to not have to specify the type given that I already have in the T::get() call.

T is a generic type parameter of the get function, which can be different from the type you've implemented the trait for. These might also compile (assuming the types in question implement FromWsm + for<'a> Deserialize<'a>)

let mut plans: Vec<String> = Plan::get(&client).await;
let mut plans: Vec<Vec<String>> = Plan::get(&client).await;
let mut plans: Vec<i32> = Plan::get(&client).await;
// and so on

If get is supposed to return the type the trait is implemented for, you need to use Self instead

    async fn get(client: &reqwest::Client) -> Vec<Self> where Self: FromWsm + for<'a> serde::Deserialize<'a> {
        client.get(&Self::url()).send().await.unwrap().json().await.unwrap()
    }
2 Likes

Thank you for the response!
If I implement the changes you suggest I end up with the opposite "problem".

pub trait FromWsm {
    async fn get<T>(client: &reqwest::Client) -> Vec<Self> where Self: FromWsm  + for<'a> Deserialize<'a> {
        client.get(&Self::url()).send().await.unwrap().json::<Vec<T>>().await.unwrap()
    }

I'm attempting to use default defintions of associated methods in the FromWsm trait to increase my code reuse.

{
    let out = Plan::get(&client).await;
}

and I end up with the inverse of my previous issue

error[E0282]: type annotations needed
   --> src/wsmservices2/plans.rs:336:30
    |
336 |         let out = Plan::get(&client).await;
    |                              ^^^^^^^^^ cannot infer type of the type parameter `T` declared on the associated function `get`
    |
help: consider specifying the generic argument
    |
336 |         let out = Plan::get::<T>(&client).await;
    |                                       +++++

where the compiler wants me (in my opinion) to overspecify, as the Plan::get::() seems quite excessive.

Just to be clear, I want to use an associated method (::url()) of the Plan type, while calling the associated default method from the FromWsm trait (::get()) and return instances of the Plan type.

You are not using the generic parameter T. Remove it.

1 Like

Thank you. I believe what you mean by remove the generic paramater T is:

pub trait FromWsm {
    async fn get(client: &reqwest::Client) -> Vec<Self> where Self: FromWsm  + for<'a> Deserialize<'a> {
        client.get(Self::url()).send().await.unwrap().json::<Vec<Self>>().await.unwrap()
    }
}

This compiles successfully for me and the compiler can infer the returned type. I learned something new about how Self works today and I thank you both for it!

1 Like

Yes, that's what I mean. The thing is, when you have a function that both takes and produces a concrete type, and doesn't depend on any other type for its operation, then it is not generic. It's just a plain old regular function where Self is a specific type. You might have directly used Plan in the signature as well.

Incidentally, Self is not magic, it is literally just the same thing as the type for which the impl is being written. Hence, if that type isn't generic, then Self isn't, either.

Note that a type isn't automatically generic just because you implement a trait for it. Generics use trait bounds for constraints to be at all usable, but traits impls don't change whether an already defined type itself is generic. These are two very different sides of the story. An impl provides a trait's concrete behavior on a type, while a generic bound constrains the set of types usable as the parameters of generic function/type.

1 Like

I think that's where I got confused. My goal was to define one set of functions that would provide the same functionality for multiple Types. I was successful in doing that, but I wasn't actually using generics to do so.

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.