如何制作一个接受异步闭包参数的方法?

问题描述 投票:0回答:1

我正在尝试为

Arc<tokio::sync::RwLock<T>>
制作一个新类型的包装器。我有一个
with()
方法,它采用非异步闭包,并且效果很好。

现在我想添加一个接受异步闭包的

with_async()
方法。但编译器报错。

这有可能吗?

use std::sync::Arc;
use tokio::sync::RwLock;
use std::future::Future;

#[derive(Debug, Default)]
pub struct AtomicRw<T>(Arc<RwLock<T>>);
impl<T> From<T> for AtomicRw<T> {
    fn from(t: T) -> Self {
        Self(Arc::new(RwLock::new(t)))
    }
}


impl<T> AtomicRw<T> {

    // This compiles.
    pub async fn with<R, F>(&self, f: F) -> R
    where
        F: FnOnce(&T) -> R,
    {
        let lock = self.0.read().await;
        f(&lock)
    }
    
    pub async fn with_async<R, F>(&self, f: F) -> R
    where
        // Error: error[E0562]: `impl Trait` only allowed in function and inherent method argument and return types, not in `Fn` trait return types
        F: FnOnce(&T) -> impl Future<Output = R>,
    {
        let lock = self.0.read().await;
        f(&lock).await
    }
    
}

#[tokio::main]
async fn main() {
    struct Car {
        year: u16,
    }

    let atomic_car = AtomicRw::from(Car{year: 2016});
    
    atomic_car.with(|c| println!("year: {}", c.year)).await;
    let _year = atomic_car.with(|c| c.year).await;
    
    atomic_car.with_async(|c| async {
        println!("year: {}", c.year)
        
    }).await;
}

游乐场

完整的错误是:

error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in `Fn` trait return types
  --> src/main.rs:29:26
   |
29 |         F: FnOnce(&T) -> impl Future<Output = R>,
   |                          ^^^^^^^^^^^^^^^^^^^^^^^


编辑:我可以通过将 fn 更改为以下内容来编译它:

    pub async fn with_async<'a, R, F>(&'a self, f: impl FnOnce(&T) -> F ) -> R
    where
        F: Future<Output = R>,
    {
        let lock = self.0.read().await;
        f(&lock).await
    }

但是,当我尝试使用它时,我仍然收到有关生命周期的编译错误:

    atomic_car.with_async(|c| async {
        println!("year: {}", c.year)
error: lifetime may not live long enough
  --> src/lib.rs:47:31
   |
47 |       atomic_car.with_async(|c| async move {
   |  ____________________________--_^
   | |                            ||
   | |                            |return type of closure `{async block@src/lib.rs:47:31: 50:6}` contains a lifetime `'2`
   | |                            has type `&'1 Car`
48 | |         println!("year: {}", c.year)
49 | |         
50 | |     }).await;
   | |_____^ returning this value requires that `'1` must outlive `'2`
asynchronous rust closures
1个回答
0
投票

好的,我找到了解决方案这里,并有详细的解释。

不幸的是,它似乎需要动态调度,并且调用者需要添加

.boxed()
并包含
FutureExt
特征,所有这些都很丑陋。

如果有人有更好的解决方案而没有这些缺点,请发布。

这有效。

use std::sync::Arc;
use tokio::sync::RwLock;
use futures::future::{BoxFuture, FutureExt};

#[derive(Debug, Default)]
pub struct AtomicRw<T>(Arc<RwLock<T>>);
impl<T> From<T> for AtomicRw<T> {
    #[inline]
    fn from(t: T) -> Self {
        Self(Arc::new(RwLock::new(t)))
    }
}


impl<T> AtomicRw<T> {
    
    pub async fn with_async<R>(&self, f: impl Fn(&'_ T) -> BoxFuture<'_, R>) -> R {
        let lock = self.0.read().await;
        f(&lock).await
    }
    
}

#[tokio::main]
async fn main() {
    struct Car {
        year: u16,
    }

    let atomic_car = AtomicRw::from(Car{year: 2016});
        
    atomic_car.with_async(|c| async {
        println!("year: {}", c.year)
    }.boxed()).await;
    
    let year2 = atomic_car.with_async(|c| async {c.year}.boxed()).await;
    assert_eq!(year2, 2016);
    
    let atomic_num = AtomicRw::from(2016u64);
    let year3 = atomic_num.with_async(|n| async {n.clone()}.boxed()).await;
    assert_eq!(year3, 2016);
}

游乐场

© www.soinside.com 2019 - 2024. All rights reserved.