使用带有 impl 特征引用作为参数的 async_recursion 宏
Using async_recursion macro with an impl trait reference as argument
我正在尝试在将 impl 特征作为参数的构造函数上使用 #[async_recursion]
宏。 impl trait 只是围绕 reqwest 的垫片,所以我可以插入一个 mock 进行测试:
#[async_trait]
pub trait NetFuncs {
async fn get(&self, url: &str) -> Result<String, Error>;
}
在我使我的构造函数递归之前它工作正常:
#[derive(Debug)]
pub struct Foo {
map: serde_yaml::mapping::Mapping,
filename: String,
parent: Option<Box<Foo>>,
receipt: Option<Receipt>,
}
impl Foo {
#[async_recursion]
pub async fn from_str(s: &str, filename: &str, net: &impl NetFuncs) -> Result<Foo, Error> {
抛出错误:
error: future cannot be sent between threads safely
--> src/main.rs:97:5
|
97 | #[async_recursion]
| ^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
|
note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
--> src/main.rs:125:17
|
125 | net,
| ^^^ has type `&impl NetFuncs` which is not `Send`, because `impl NetFuncs` is not `Sync`
= note: required for the cast to the object type `dyn Future<Output = Result<Foo, Error>> + Send`
= note: this error originates in the attribute macro `async_recursion` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting this bound
|
98 | pub async fn from_str(s: &str, filename: &str, net: &impl NetFuncs + std::marker::Sync) -> Result<Foo, Error> {
| +++++++++++++++++++
还有其他方法可以像我那样模拟网络进行测试,但我喜欢我的解决方案,至少在遇到此错误之前是这样。如何在不删除 net: &impl NetFuncs
参数的情况下修复此错误?
MRE
[package]
name = "mre2"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-recursion = "1.0"
async-trait = "0.1"
use async_trait::async_trait;
use async_recursion::async_recursion;
#[derive(Debug)]
pub struct Foo {
s: String,
filename: String,
foo: String,
parent: Option<Box<Foo>>,
}
#[async_trait]
pub trait NetFuncs {
async fn get(&self, url: &str) -> String;
}
#[derive(Debug)]
pub struct FakeNet {}
#[async_trait]
impl NetFuncs for FakeNet {
async fn get(&self, url: &str) -> String {
"".to_string()
}
}
impl Foo {
#[async_recursion]
pub async fn from_str(s: &str, filename: &str, net: &impl NetFuncs) -> Foo {
Foo { s: s.to_string(), filename: filename.to_string(), parent: None, foo: net.get("").await.to_string() }
}
}
问题是,正如编译器所解释的那样,&impl NetFuncs
可能不一定会实现 Send
但默认情况下 async_recursion
宏需要它,因此,您有两个选择:
- 要求
impl NetFuncs
为Sync
,因此&impl NetFuncs
为Send
。这可以通过 &(impl NetFuncs + Sync)
或要求每个实施者实施 Send
来完成:trait NetFuncs: Sync
.
- 不要求结果未来是
Send
。 As documented in the async_recursion
documentation,这可以通过将 #[async_recursion]
更改为 #[async_recursion(?Send)]
来完成。
没有宏它就可以工作,因为编译器生成的未来 Send
取决于 .await
点上的所有类型是否都是 Send
:如果是,未来也是Send
。如果不是,那也不是。宏将 async fn
更改为 fn ...() -> Pin<Box<dyn Future>>
,不幸的是,不可能具有与 async fn
相同的行为 - 这只有编译器才能实现。因此,该宏允许您选择是否希望结果未来成为 Send
- 意味着所有类型都应该是,还是不是。
我正在尝试在将 impl 特征作为参数的构造函数上使用 #[async_recursion]
宏。 impl trait 只是围绕 reqwest 的垫片,所以我可以插入一个 mock 进行测试:
#[async_trait]
pub trait NetFuncs {
async fn get(&self, url: &str) -> Result<String, Error>;
}
在我使我的构造函数递归之前它工作正常:
#[derive(Debug)]
pub struct Foo {
map: serde_yaml::mapping::Mapping,
filename: String,
parent: Option<Box<Foo>>,
receipt: Option<Receipt>,
}
impl Foo {
#[async_recursion]
pub async fn from_str(s: &str, filename: &str, net: &impl NetFuncs) -> Result<Foo, Error> {
抛出错误:
error: future cannot be sent between threads safely
--> src/main.rs:97:5
|
97 | #[async_recursion]
| ^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
|
note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
--> src/main.rs:125:17
|
125 | net,
| ^^^ has type `&impl NetFuncs` which is not `Send`, because `impl NetFuncs` is not `Sync`
= note: required for the cast to the object type `dyn Future<Output = Result<Foo, Error>> + Send`
= note: this error originates in the attribute macro `async_recursion` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting this bound
|
98 | pub async fn from_str(s: &str, filename: &str, net: &impl NetFuncs + std::marker::Sync) -> Result<Foo, Error> {
| +++++++++++++++++++
还有其他方法可以像我那样模拟网络进行测试,但我喜欢我的解决方案,至少在遇到此错误之前是这样。如何在不删除 net: &impl NetFuncs
参数的情况下修复此错误?
MRE
[package]
name = "mre2"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-recursion = "1.0"
async-trait = "0.1"
use async_trait::async_trait;
use async_recursion::async_recursion;
#[derive(Debug)]
pub struct Foo {
s: String,
filename: String,
foo: String,
parent: Option<Box<Foo>>,
}
#[async_trait]
pub trait NetFuncs {
async fn get(&self, url: &str) -> String;
}
#[derive(Debug)]
pub struct FakeNet {}
#[async_trait]
impl NetFuncs for FakeNet {
async fn get(&self, url: &str) -> String {
"".to_string()
}
}
impl Foo {
#[async_recursion]
pub async fn from_str(s: &str, filename: &str, net: &impl NetFuncs) -> Foo {
Foo { s: s.to_string(), filename: filename.to_string(), parent: None, foo: net.get("").await.to_string() }
}
}
问题是,正如编译器所解释的那样,&impl NetFuncs
可能不一定会实现 Send
但默认情况下 async_recursion
宏需要它,因此,您有两个选择:
- 要求
impl NetFuncs
为Sync
,因此&impl NetFuncs
为Send
。这可以通过&(impl NetFuncs + Sync)
或要求每个实施者实施Send
来完成:trait NetFuncs: Sync
. - 不要求结果未来是
Send
。 As documented in theasync_recursion
documentation,这可以通过将#[async_recursion]
更改为#[async_recursion(?Send)]
来完成。
没有宏它就可以工作,因为编译器生成的未来 Send
取决于 .await
点上的所有类型是否都是 Send
:如果是,未来也是Send
。如果不是,那也不是。宏将 async fn
更改为 fn ...() -> Pin<Box<dyn Future>>
,不幸的是,不可能具有与 async fn
相同的行为 - 这只有编译器才能实现。因此,该宏允许您选择是否希望结果未来成为 Send
- 意味着所有类型都应该是,还是不是。