我可以将函数参数的Send
特性传播到它的返回类型,使得当且仅当参数为时,返回类型为impl Send
吗?
详细数据:
异步函数有一个很好的特性。如果可以的话,它返回的Future
会自动为Send
。在下面的例子中,如果函数的输入为Send
,异步函数将创建一个Future
,即Send
。
struct MyStruct;
impl MyStruct {
// This async fn returns an `impl Future<Output=T> + Send` if `T` is Send.
// Otherwise, it returns an `impl Future<Output=T>` without `Send`.
async fn func<T>(&self, t: T) -> T {
t
}
}
fn assert_is_send(_v: impl Send) {}
fn main() {
// This works
assert_is_send(MyStruct.func(4u64));
// And the following correctly fails
assert_is_send(MyStruct.func(std::rc::Rc::new(4u64)));
}
playground
现在,我想把这样的功能转移到一个特征上,这需要使用异步特性(这是一个代码生成器,它可以有效地将我的async fn
编写为返回Pin<Box<dyn Future>>
的函数)或手动执行类似的操作。如果T
为Send
,则传回的Future
会变成Send
的传送行为?下列范例会将它实作为两个不同的函数:
use std::pin::Pin;
use std::future::Future;
struct MyStruct;
impl MyStruct {
fn func_send<T: 'static + Send>(&self, t: T) -> Pin<Box<dyn Future<Output = T> + Send>> {
Box::pin(async{t})
}
fn func_not_send<T: 'static>(&self, t: T) -> Pin<Box<dyn Future<Output = T>>> {
Box::pin(async{t})
}
}
fn assert_is_send(_v: impl Send) {}
fn main() {
// This works
assert_is_send(MyStruct.func_send(4u64));
// And the following correctly fails
// assert_is_send(MyStruct.func(std::rc::Rc::new(4u64)));
}
playground
但实际上,我并不希望它们是分开的,我希望它们是一个类似于async fn
自动执行的函数。
use std::pin::Pin;
use std::future::Future;
struct MyStruct;
impl MyStruct {
fn func<T: 'static + ?Send>(&self, t: T) -> Pin<Box<dyn Future<Output = T> + ?Send>> {
Box::pin(async{t})
}
}
fn assert_is_send(_v: impl Send) {}
fn main() {
// This should
assert_is_send(MyStruct.func(4u64));
// And this should fail
assert_is_send(MyStruct.func(std::rc::Rc::new(4u64)));
}
在Rust中有这样的可能吗?我可以手动编写async-trait magic并修改它,而不是使用async-trait crate,如果这是一种使它工作的方式。
我有一些想法,但它们还没有真正开花结果:
- 使用min-specialization来专门化
Send
?但是看起来这个特性在短期内不会稳定下来,所以可能不是最好的选择。 - 返回一个自定义的
MyFuture
类型,而不仅仅是impl Future
和impl Send for MyFuture where T: Send
?这可能很困难,因为我必须能够命名Future
,而async
代码通常会生成无法命名的impl Future
类型。 - 编写一个过程宏,如果它识别出输入类型是
Send
,就将+ Send
添加到返回类型。实际上,过程宏能检测到某个类型是否实现了Send
吗?我猜这是不可能的,因为它们只对令牌流起作用。
1条答案
按热度按时间ctzwtxfj1#
(2)是唯一可行的办法。
有两种方法可以使其工作:
1.手动书写未来,而不需要
async
和.await
的帮助,但这意味着手动书写未来:Playground。
1.存储一个
Pin<Box<dyn Future<Output = T>>>
,并在将来有条件地实现Send
。但这需要unsafe
代码,并手动确保在.await
点上不持有其他非Send
类型:是的。
(1)我不能和特性一起工作,因为每个impl将有不同的未来。2这只给我们留下了(2)。3我不推荐它,但它是可能的。
当traits中的异步fns稳定时,很可能会有一种机制(目前讨论的是有条件地实现它们,并在使用站点上使用边界来要求它们),但目前还没有这样的机制,即使在traits中的异步fns的夜间实现上也是如此。