asynchronous - 在需要特征对象的上下文中使用借用参数的异步函数
问题描述
我最近开始在 Rust 中使用异步流,并且一直发现自己处于想要在Stream
. 异步函数通常来自我无法控制的库,但为了举例,假设它们看起来像这样:
async fn bar_str(s: &str) -> String {
s.to_string()
}
async fn bar_string(s: String) -> String {
s
}
另外为了简单起见,假设我只是想使用这些函数来实现如下特征(不涉及实际的流内容):
use std::future::Future;
trait Foo {
fn bar(self) -> Box<dyn Future<Output = String>>;
}
对于这种String
情况,这就像您期望的那样:
impl Foo for String {
fn bar(self) -> Box<dyn Future<Output = String>> {
Box::new(bar_string(self))
}
}
对于异步函数借用的情况,它不会。
impl Foo for &str {
fn bar(self) -> Box<Future<Output = String>> {
Box::new(bar_str(self))
}
}
这无法编译:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter '_ in function call due to conflicting requirements
--> foo.rs:23:18
|
23 | Box::new(bar_str(self))
| ^^^^^^^^^^^^^
|
...
我可以理解为什么这是一个问题,并且我理解async fn
语法为这样的借用参数提供了特殊处理(尽管我对它的实际检查、脱糖等一无所知)。
我的问题是关于在这些情况下最好的做法是什么。有什么方法可以重现async fn
我的非async fn
代码中的魔力吗?我是否应该避免借用异步函数(如果可以,因为这通常是我没有做出的决定)?在我目前正在编写的代码中,如果它们使阅读和编写这种事情变得更好,我很乐意使用实验性或非必要的面向未来的解决方案。
解决方案
我认为问题不在于事情,async
而在于Box<dyn Trait>
事情。事实上,它可以通过一个简单的特征来复制:
use std::fmt::Debug;
trait Foo {
fn foo(self) -> Box<dyn Debug>;
}
impl Foo for String {
fn foo(self) -> Box<dyn Debug> {
Box::new(self)
}
}
impl Foo for &str {
fn foo(self) -> Box<dyn Debug> {
Box::new(self) // <--- Error here (line 15)
}
}
完整的错误信息是:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/lib.rs:15:18
|
15 | Box::new(self)
| ^^^^
|
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the impl at 13:14...
--> src/lib.rs:13:14
|
13 | impl Foo for &str {
| ^
note: ...so that the expression is assignable
--> src/lib.rs:15:18
|
15 | Box::new(self)
| ^^^^
= note: expected `&str`
found `&str`
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
--> src/lib.rs:15:9
|
15 | Box::new(self)
| ^^^^^^^^^^^^^^
= note: expected `std::boxed::Box<(dyn std::fmt::Debug + 'static)>`
found `std::boxed::Box<dyn std::fmt::Debug>`
关于最后两行发生的事情有一个很好的暗示......这是什么Box<(dyn Debug + 'static)>
东西?
当您编写dyn Trait
时,实际上对实现该特征的类型有一个隐式约束,因此这两个是同一件事: 'static
Box<dyn Debug>
Box<(dyn Debug + 'static)>
但这意味着我们只能对类型为 的值进行装箱'static
。而且&'a str
不是静态类型,所以不能这样装箱。
像往常一样,简单的解决方案是克隆,如果可能的话。这编译并且它不是太难看:
impl Foo for &str {
fn foo(self) -> Box<dyn Debug> {
Box::new(self.to_owned())
}
}
或者,如果您只使用静态字符串,那么&'static str
实际上是静态的,您可以编写:
impl Foo for &'static str {
fn foo(self) -> Box<dyn Debug> {
Box::new(self)
}
}
如果您确实想要或需要借用,那么装箱的 dyn 对象必须在某个生命周期内是通用的。您必须更改特征的返回类型,如下所示:
use std::fmt::Debug;
trait Foo<'a> {
fn foo(self) -> Box<dyn Debug + 'a>;
}
impl Foo<'static> for String {
fn foo(self) -> Box<dyn Debug> {
Box::new(self)
}
}
impl<'a> Foo<'a> for &'a str {
fn foo(self) -> Box<dyn Debug + 'a> {
Box::new(self)
}
}
但现在要注意,Box<dyn Debug + 'a>
它本身不是静态类型,而是类型本身的生命周期为'a
.
推荐阅读
- javascript - 在带有 PKCE 的 OAuth 授权流中使用时,如何在 Azure 应用注册中启用 CORS?
- excel - 如果范围内的单元格不等于空白,则将字符串应用于另一个范围内的单元格
- java - 带有 fcm 的 Android 网页视图
- erlang - 如何在 Erlang 中修复不正确的列表?
- azure-active-directory - 在两个 AD B2C 租户之间共享本地帐户
- mysql - 如何在 laravel 中运行多个查询
- c# - 如何在 Linq 中编写此代码以减少代码?
- python-3.x - 使用 Python 在配置文件底部读取未知数量的行
- php - 如何将分页从“每页”更改为只有第一页和最后一页?
- java - 在java compareTo中通过多种方法进行比较?