英文:
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<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() {
}
When compiling this code, I get the following error:
error[E0308]: mismatched types
--> 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<'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,
| ^^^
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<'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)
}
}
Then I can use this trait to constraints lifetime on arguments and Future like this:
fn record_callback<F>(callback: F)
where
for<'a> F: XFn<'a, &'a mut FirstTestComponent, &'a mut Services> + 'static + Copy,
{
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论