我正在尝试为
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`
好的,我找到了解决方案这里,并有详细的解释。
不幸的是,它似乎需要动态调度,并且调用者需要添加
.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);
}