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.
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
.