Search code examples
rustrust-analyzer

Is there any way to track the monomorphization process of generics?


I find it is untrackable by just reading the plain source code, when the generic relationship is convoluted.

Is there any way to disclose the compiler's instantiation trajectory of a particular generic struct or function?

Elaborated:

Thanks for interesting in this quesion. It is elaborated here. I'm studying the Warp crate recently. So take it for example.

use warp::Filter;

async fn handler_without_parameter() -> Result<impl warp::Reply, warp::Rejection> {
    Ok(warp::reply::json(&"dumb".to_string()))
}

fn main() {
    let get_items = warp::get()
        .and(warp::path("/"))
        .and(warp::path::end())
        .and_then(handler_without_parameter); //Ok here
}
async fn handler_without_parameter() -> Result<impl warp::Reply, warp::Rejection> {
    Ok(warp::reply::json(&"dumb".to_string()))
}

fn main() {
    let para: HashMap<String, String> = HashMap::new();
    let para_filter = warp::any().map(move || para.clone()); // A filter with data

    let get_items = warp::get()
        .and(warp::path("/"))
        .and(warp::path::end())
        .and(para_filter)      //chain with the data filter
        .and_then(handler_without_parameter); // error[E0593]: function is expected to take 1 argument, but it takes 0 arguments
}

After chaining a filter( which taking along with data), the parameter of the method and_then is expected to take 1 argument. I'm curious about this requirement change for and_then, so I try to figure out the monomorphization course of this method.

https://docs.rs/warp/0.3.5/warp/trait.Filter.html#method.and_then

    fn and<F>(self, other: F) -> And<Self, F>
    where
        Self: Sized,
        <Self::Extract as Tuple>::HList: Combine<<F::Extract as Tuple>::HList>,
        F: Filter + Clone,
        F::Error: CombineRejection<Self::Error>,
    {
        And {
            first: self,
            second: other,
        }
    }


    fn and_then<F>(self, fun: F) -> AndThen<Self, F>
    where
        Self: Sized,
        F: Func<Self::Extract> + Clone,
        F::Output: TryFuture + Send,
        <F::Output as TryFuture>::Error: CombineRejection<Self::Error>,
    {
        AndThen {
            filter: self,
            callback: fun,
        }
    }


And the base trait is:

pub trait FilterBase {
    type Extract: Tuple; // + Send;
    type Error: IsReject;
    type Future: Future<Output = Result<Self::Extract, Self::Error>> + Send;

    fn filter(&self, internal: Internal) -> Self::Future;

    fn map_err<F, E>(self, _internal: Internal, fun: F) -> MapErr<Self, F>
    ...
}

I can't figure out why the added chaining could incur the requirement change of the and_then method, since, to me, the generics relation here is very convoluted.

Even with the hints provided by rust-analyzer, it doesn't help a lot, because the Extract = ... part is eclipsed. enter image description here


Solution

  • The issue is that para_filter returns something, and the next function has to take what the previous function returns. So if para_filter returns a HashMap<String, String>, then handler_without_parameter must take HashMap<String, String> as its sole argument. If para_filter returned nothing, then handler_without_parameter would have to take an empty tuple as its sole argument (handler_without_parameter(_: ()) -> ...).

    How I figured this out: the rather opaque error message

    error[E0593]: function is expected to take 1 argument, but it takes 0 arguments
      --> src/main.rs:16:10
       |
    4  | async fn handler_without_parameter() -> Result<impl warp::Reply, warp::Rejection> {
       | --------------------------------------------------------------------------------- takes 0 arguments
    ...
    16 |         .and_then(handler_without_parameter); // error[E0593]: function is expected to take 1 argument, but it takes 0 arguments
       |          ^^^^^^^^ expected function that takes 1 argument
       |
       = note: required for `fn() -> impl Future<Output = Result<impl Reply, Rejection>> {handler_without_parameter}` to implement `warp::generic::Func<(HashMap<String, String>,)>`
    

    The key is the last line, note: required for ... {handler_without_parameter} to implement warp::generic::Func<(HashMap<String, String>,)> which is cut off in your screenshot — when in doubt, best to always read the full compiler diagnostic. If you're using rust analyzer, then you can hover over an error and click “show full compiler diagnostic”.

    As an aside, this does not have to do with monomorphization, but rather with generic functions whose input type and output type share a generic parameter and/or generic constraint. The argument to and affects the type of its output, which in turn affects the type of the object calling and_then, which in turn affects the type of the argument expected by and_then.