在Rust中传递异步函数项作为参数时出现问题。

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

Problem passing an async function item as parameter in Rust

问题

我尝试将一个异步函数作为参数传递给Rust中的另一个函数,但它无法编译,出现了一个晦涩的错误。

以下是我试图编译的代码。

结构定义(在一个crate中实现)

pub struct FirstTestComponent {
    table: Vec<String>,
    counter: usize,
}

impl FirstTestComponent {
    fn render(&mut self) {
        // Some other irrelevant code
        record_callback(
            FirstTestComponent::add_component,
        );
    }
}

pub struct Services {}

impl FirstTestComponent {

    async fn add_component(&mut self, _: &mut Services) {

        let counter = self.counter;
        self.table.push(counter.to_string());
        self.counter += 1;
    }
}   

pub fn record_callback<F, Fut>(callback: F)
    where 
        F: 'static + Copy + FnOnce(&mut FirstTestComponent, &mut Services) -> Fut,
        Fut: core::future::Future<Output = ()> + 'static
        {
        }

fn main() {

}

编译这段代码时,我得到了以下错误:

error[E0308]: mismatched types
  --> src/main.rs:9:9
   |
9  |         record_callback(
   |         ^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected trait `for<'r, 's> <for<'r, 's> fn(&'r mut FirstTestComponent, &'s mut Services) -> impl for<'r, 's> std::future::Future<Output = ()> {FirstTestComponent::add_component} as std::ops::FnOnce((&'r mut FirstTestComponent, &'s mut Services))>`
              found trait `for<'r, 's> <for<'r, 's> fn(&'r mut FirstTestComponent, &'s mut Services) -> impl for<'r, 's> std::future::Future<Output = ()> {FirstTestComponent::add_component} as std::ops::FnOnce((&'r mut FirstTestComponent, &'s mut Services))>`
note: the lifetime requirement is introduced here
  --> src/main.rs:30:79
   |
30 |         F: 'static + Copy + FnOnce(&mut FirstTestComponent, &mut Services) -> Fut,
   |                                                                               ^^^

字面上说有些东西与其本身不同...

我猜测错误消息中隐藏了与生命周期有关的错误,但我无法弄清楚。

可能是什么原因导致了这个错误?我实现的代码有什么问题?

英文:

[Edit: Updated provided code and compiler error to be easely reproduced]

I'm trying to pass an async function item as parameter to an other function in Rust but it won't compile, providing a cryptic error.

Here is the code I'm trying to compile.

Structure definition (implemented in one crate)

pub struct FirstTestComponent {
    table: Vec&lt;String&gt;,
    counter: usize,
}

impl FirstTestComponent {
    fn render(&amp;mut self) {
        // Some other irrelevant code
        record_callback(
            FirstTestComponent::add_component,
        );
    }
}

pub struct Services {}

impl FirstTestComponent {

    async fn add_component(&amp;mut self, _: &amp;mut Services) {

        let counter = self.counter;
        self.table.push(counter.to_string());
        self.counter += 1;
    }
}   


pub fn record_callback&lt;F, Fut&gt;(callback: F)
    where 
        F: &#39;static + Copy + FnOnce(&amp;mut FirstTestComponent, &amp;mut Services) -&gt; Fut,
        Fut: core::future::Future&lt;Output = ()&gt;  + &#39;static
        {
        }

fn main() {

}

When compiling this code, I get the following error:

error[E0308]: mismatched types
  --&gt; src/main.rs:9:9
   |
9  | /         record_callback(
10 | |             FirstTestComponent::add_component,
11 | |         );
   | |_________^ one type is more general than the other
   |
   = note: expected trait `for&lt;&#39;r, &#39;s&gt; &lt;for&lt;&#39;r, &#39;s&gt; fn(&amp;&#39;r mut FirstTestComponent, &amp;&#39;s mut Services) -&gt; impl for&lt;&#39;r, &#39;s&gt; std::future::Future&lt;Output = ()&gt; {FirstTestComponent::add_component} as std::ops::FnOnce&lt;(&amp;&#39;r mut FirstTestComponent, &amp;&#39;s mut Services)&gt;&gt;`
              found trait `for&lt;&#39;r, &#39;s&gt; &lt;for&lt;&#39;r, &#39;s&gt; fn(&amp;&#39;r mut FirstTestComponent, &amp;&#39;s mut Services) -&gt; impl for&lt;&#39;r, &#39;s&gt; std::future::Future&lt;Output = ()&gt; {FirstTestComponent::add_component} as std::ops::FnOnce&lt;(&amp;&#39;r mut FirstTestComponent, &amp;&#39;s mut Services)&gt;&gt;`
note: the lifetime requirement is introduced here
  --&gt; src/main.rs:30:79
   |
30 |         F: &#39;static + Copy + FnOnce(&amp;mut FirstTestComponent, &amp;mut Services) -&gt; Fut,
   |                                                                               ^^^

literally saying that something is different than itself...

I guess there is some error linked to the lifetime hidden in the error message but I can't figure it out.

What could cause this error ? What is wrong with what I've implemented ?

答案1

得分: 0

我在这里找到了答案:https://stackoverflow.com/questions/70746671/how-to-bind-lifetimes-of-futures-to-fn-arguments-in-rust

问题我理解如下:
根据https://rust-lang.github.io/async-book/03_async_await/01_chapter.html:

与传统函数不同,接受引用或其他非'静态'参数的async fn返回的Future的生命周期受参数的生命周期限制

因此,我不得不强制Fut的生命周期比输入参数的生命周期短。

语法不允许我简单地通过使用Higher Ranked Lifetime bounds(如上面链接的答案中所解释的)来强制执行此操作,所以我不得不使用那里提出的模式:生成一个元trait将生命周期链接为如下所示:

trait XFn<'a, T, S> {
    type Output: Future<Output = ()> + 'a;
    fn call(&self, this: T, services: S) -> Self::Output;
  }
  
  impl<'a, T: 'a, S: 'a, F, Fut> XFn<'a, T, S> for F
  where
    F: 'static + Copy + FnOnce(T, S) -> Fut,
    Fut: Future<Output = ()> + 'a,
  {
    type Output = Fut;
    fn call(&self, this: T, services: S) -> Fut {
        self(this, services)
    }
  }

然后,我可以使用这个trait来约束参数和Future的生命周期,如下所示:

fn record_callback<F>(callback: F)
    where 
            for<'a> F: XFn<'a, &'a mut FirstTestComponent, &'a mut Services> + 'static + Copy,
    {
    }
英文:

I found the answer here: https://stackoverflow.com/questions/70746671/how-to-bind-lifetimes-of-futures-to-fn-arguments-in-rust

The problem as I understand it is this:
From https://rust-lang.github.io/async-book/03_async_await/01_chapter.html:
> Unlike traditional functions, async fns which take references or other non-'static arguments return a Future which is bounded by the lifetime of the arguments

So I had to force the Fut lifetime to be shorter than the input parameters lifetime.

The syntax won't let me simply force this by using Higher Ranked Lifetime bounds (as explained in the answer linked above) so I had to use the pattern proposed there: generate a meta trait linking lifetimes as this:

trait XFn&lt;&#39;a, T, S&gt; {
    type Output: Future&lt;Output = ()&gt; + &#39;a;
    fn call(&amp;self, this: T, services: S) -&gt; Self::Output;
  }
  
  impl&lt;&#39;a, T: &#39;a, S: &#39;a, F, Fut&gt; XFn&lt;&#39;a, T, S&gt; for F
  where
    F: &#39;static + Copy + FnOnce(T, S) -&gt; Fut,
    Fut: Future&lt;Output = ()&gt; + &#39;a,
  {
    type Output = Fut;
    fn call(&amp;self, this: T, services: S) -&gt; Fut {
        self(this, services)
    }
  }

Then I can use this trait to constraints lifetime on arguments and Future like this:

fn record_callback&lt;F&gt;(callback: F)
    where 
            for&lt;&#39;a&gt; F: XFn&lt;&#39;a, &amp;&#39;a mut FirstTestComponent, &amp;&#39;a mut Services&gt; + &#39;static + Copy,
    {
    }

huangapple
  • 本文由 发表于 2023年6月1日 18:06:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76380801.html
匿名

发表评论

匿名网友

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

确定