I'm trying to use the #[async_recursion] macro on a constructor that takes an impl trait as an argument. The impl trait is just a shim around reqwest so I can insert a mock for testing:
#[async_trait]
pub trait NetFuncs {
async fn get(&self, url: &str) -> Result<String, Error>;
}
It was working fine until I made my constructor recursive:
#[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> {
throws the 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> {
| +++++++++++++++++++
There are other ways to mock a network for testing then the way I did it, but I liked my solution, at least until I hit this error. How do I fix this error without removing the net: &impl NetFuncs argument?
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() }
}
}