rust 我怎样才能创建一个接受闭包参数的方法?

h5qlskok  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(160)

我正在尝试为Arc<tokio::sync::RwLock<T>>做一个新的类型 Package 器,我有一个with()方法,它接受一个非闭包,这个方法工作得很好。
现在我想添加一个with_async()方法,它接受一个codec闭包。但是编译器给出了一个错误。
这可能吗?

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;
}

字符串
Playground
完整的错误是:

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>,
   |                          ^^^^^^^^^^^^^^^^^^^^^^^


edit:我可以通过将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`

的字符串

snvhrwxg

snvhrwxg1#

好的,我找到了一个有详细解释的解决方案here
不幸的是,它似乎需要动态调度,调用者需要添加.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);
}

字符串
Playground

相关问题