有没有办法追踪泛型的单态化过程?

huangapple go评论63阅读模式
英文:

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.

Read more

    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() -&gt; Result&lt;impl warp::Reply, warp::Rejection&gt; {
    Ok(warp::reply::json(&amp;&quot;dumb&quot;.to_string()))
}

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

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

    let get_items = warp::get()
        .and(warp::path(&quot;/&quot;))
        .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&lt;F&gt;(self, other: F) -&gt; And&lt;Self, F&gt;
    where
        Self: Sized,
        &lt;Self::Extract as Tuple&gt;::HList: Combine&lt;&lt;F::Extract as Tuple&gt;::HList&gt;,
        F: Filter + Clone,
        F::Error: CombineRejection&lt;Self::Error&gt;,
    {
        And {
            first: self,
            second: other,
        }
    }


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


And the base trait is:

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

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

    fn map_err&lt;F, E&gt;(self, _internal: Internal, fun: F) -&gt; MapErr&lt;Self, F&gt;
    ...
}

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&lt;String, String&gt;,那么 handler_without_parameter 必须以 HashMap&lt;String, String&gt; 作为其唯一参数。如果 para_filter 什么都不返回,那么 handler_without_parameter 必须以一个空元组作为其唯一参数(handler_without_parameter(_: ()) -&gt; ...)。

我是如何解决这个问题的:这个相当模糊的错误信息

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

关键是最后一行,note: required for ... {handler_without_parameter} to implement warp::generic::Func&lt;(HashMap&lt;String, String&gt;,)&gt;,在你的截图中被截断了 — 如果不确定,最好总是阅读完整的编译器诊断信息。如果你正在使用 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&lt;String, String&gt;, then handler_without_parameter must take HashMap&lt;String, String&gt; 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(_: ()) -&gt; ...).

How I figured this out: the rather opaque error message

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

The key is the last line, note: required for ... {handler_without_parameter} to implement warp::generic::Func&lt;(HashMap&lt;String, String&gt;,)&gt; 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.

huangapple
  • 本文由 发表于 2023年5月28日 09:33:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76349634.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定