英文:
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.
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)
...
}
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.
英文:
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.
答案1
得分: 1
问题在于 para_filter
返回了某个值,而下一个函数必须使用前一个函数返回的值。因此,如果 para_filter
返回一个 HashMap<String, String>
,那么 handler_without_parameter
必须以 HashMap<String, String>
作为其唯一参数。如果 para_filter
什么都不返回,那么 handler_without_parameter
必须以一个空元组作为其唯一参数(handler_without_parameter(_: ()) -> ...
)。
我是如何解决这个问题的:这个相当模糊的错误信息
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>,)>`
关键是最后一行,note: required for ... {handler_without_parameter} to implement warp::generic::Func<(HashMap<String, String>,)>
,在你的截图中被截断了 — 如果不确定,最好总是阅读完整的编译器诊断信息。如果你正在使用 Rust Analyzer,那么你可以将鼠标悬停在错误上,然后点击“显示完整的编译器诊断”。
另外,这与单态化无关,而是与泛型函数有关,其输入类型和输出类型共享一个泛型参数和/或泛型约束。and
的参数会影响其输出的类型,进而影响调用 and_then
的对象的类型,从而影响 and_then
期望的参数类型。
英文:
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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论