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() }
}
}
The problem is, like explained by the compiler, that &impl NetFuncs may not necessarily impl Send but the async_recursion macro by default requires it, so, you have two options:
Require impl NetFuncs to be Sync, so that &impl NetFuncs is Send. This can be done either with &(impl NetFuncs + Sync) or by requiring every implementor to implement Send: trait NetFuncs: Sync.
Not requiring the resulting future to be Send. As documented in the async_recursion documentation, this can be done by changing #[async_recursion] to #[async_recursion(?Send)].
Without the macro it works since the compiler make the resulting future Send depend on whether all types kept across .await points are Send: if they are, the future is also Send. If they are not, it is not too. The macro changes the async fn to fn ...() -> Pin<Box<dyn Future>>, and unfortunately, it is not possible to have the same behavior as with async fn - this is something only the compiler can implement. Thus, the macro allows you to choose whether you want the resulting future to be Send - meaning all types should be too, or not.
Related
This question already has answers here:
Why are trait methods with generic type parameters object-unsafe?
(2 answers)
Closed 2 months ago.
I have this signature that I wanna use in a Trait to create many adapters (file system, aws, google cloud etc.).
I'm trying to create the below Trait but this error occurs:
use tokio::io::AsyncRead;
#[async_trait::async_trait]
pub trait Trait: Send + Sync {
async fn put_file<S>(&self, filename: &str, stream: S) -> Result<()>
where
S: AsyncRead + Send;
}
pub struct Client {
location: String,
}
#[async_trait::async_trait]
impl Trait for Client {
async fn put_file<S>(&self, filename: &str, stream: S) -> Result<()>
where
S: AsyncRead + Send,
{
futures::pin_mut!(stream);
let path = Path::new(&self.location).join(filename);
let mut file = BufWriter::new(File::create(path).await?);
tokio::io::copy(&mut stream, &mut file).await?;
Ok(())
}
}
error[E0038]: the trait `Trait` cannot be made into an object
|
23 | pub client: Arc<dyn Trait>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
|
5 | pub trait Trait: Send + Sync {
| ----- this trait cannot be made into an object...
6 | async fn put_file<S>(&self, filename: &str, stream: S) -> Result<()>
| ^^^^^^^^ ...because method `put_file` has generic type parameters
= help: consider moving `put_file` to another trait
Why?
If you read your error message it will both mention that it's
...because method put_file has generic type parameters
as well as where you can find further info on object safety
You can just not create a trait object instead:
struct YourStructThatContainsTrait<T: Trait> {
pub client: Arc<T>,
}
Or go with dynamic dispatch all the way:
async fn put_file(&self, filename: &str, stream: Box<dyn AsyncRead + Unpin + Send>) -> Result<()>;
I have a trait Serializer:
trait Serializer {
fn serialize(&self, data: Rc<()>) -> Result<(), Error>;
}
And some Foo which implements it. Notably, data is not Send.
The actual implementation requires an async context to execute, so I've split the creation of an async runtime away from the actual implementation, like so:
struct Foo {}
impl Foo {
async fn async_serialize(&self, _data: Rc<()>) -> Result<(), Error> {
unimplemented!();
}
}
impl Serializer for Foo {
fn serialize(&self, data: Rc<()>) -> Result<(), Error> {
let runtime = Builder::new_current_thread().enable_all().build().unwrap();
runtime.block_on(async move { self.async_serialize(data).await })
}
}
This compiles and works how I would expect.
Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=04171e7fb65a9e158903978fd19ef8ac
If I refactor the code such that the async Runtime creation is done by way of a trait:
#[async_trait]
trait AsyncSerializer {
async fn async_serialize(&self, data: Rc<()>) -> Result<(), Error>;
}
#[async_trait]
impl AsyncSerializer for Foo {
async fn async_serialize(&self, _data: Rc<()>) -> Result<(), Error> {
unimplemented!();
}
}
This does not compile, now complaining that Rc<()> isn't Send:
error: future cannot be sent between threads safely
--> src/main.rs:19:73
|
19 | async fn async_serialize(&self, _data: Rc<()>) -> Result<(), Error> {
| _________________________________________________________________________^
20 | | unimplemented!();
21 | | }
| |_____^ future created by async block is not `Send`
|
= help: within `impl Future<Output = Result<(), anyhow::Error>>`, the trait `Send` is not implemented for `Rc<()>`
note: captured value is not `Send`
Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=23f61162b5be8384092888635d8daacc
This error message makes sense to me, Rc is not Send, but:
Why was this not a problem before? The prior implementations (impl on Foo versus impl AsyncSerializer for Foo) look analogous to me.
Can I wrap data in some way to avoid this?
The way that #[async_trait] de-sugars your code requires your futures to be send, as explained here. To fix that change the attribute macro to be #[async_trait(?Send)].
On this simple sketch I made, I'm trying to do use a trait called Runnable to run an Arc<dyn LockableOption<T>>:
use std::sync::{Arc, LockResult, Mutex, MutexGuard, PoisonError};
pub type LockableArc<T> = Arc<Mutex<Option<T>>>;
pub struct MutexGuardOptionRef<'a, T: ?Sized> {
pub mutex_guard: MutexGuard<'a, Option<Box<T>>>,
}
pub trait LockableOption<T: ?Sized>: Send + Sync {
fn lock(&self) -> LockResult<MutexGuardOptionRef<T>>;
}
impl<T: ?Sized + Send> LockableOption<T> for LockableArc<Box<T>> {
fn lock(&self) -> LockResult<MutexGuardOptionRef<T>> {
unimplemented!()
}
}
pub trait Decoder<T>: Send {
}
pub struct FfmpegDecoder<T> {
x: T,
}
impl<T: 'static + Send> Decoder<T> for FfmpegDecoder<T> {
}
trait DecoderRunnable<T> {
fn run(s: Arc<dyn LockableOption<dyn Decoder<T>>>);
}
impl<T: 'static + Send> DecoderRunnable<T> for FfmpegDecoder<T> {
fn run(s_lockable: Arc<dyn LockableOption<dyn Decoder<T>>>) {
unimplemented!()
}
}
fn main() {
let r: LockableArc<Box<dyn Decoder<u8>>> = Arc::new(Mutex::new(Some(Box::new(FfmpegDecoder{x: 0u8}))));
let rr: Arc<dyn LockableOption<dyn Decoder<u8>>> = Arc::new(r);
DecoderRunnable::<u8>::run(rr.clone());
}
Playground
I get the error:
error[E0283]: type annotations needed
--> src/main.rs:42:5
|
30 | fn run(s: Arc<dyn LockableOption<dyn Decoder<T>>>);
| --------------------------------------------------- required by `DecoderRunnable::run`
...
42 | DecoderRunnable::<u8>::run(rr.clone());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
|
= note: cannot satisfy `_: DecoderRunnable<u8>`
which I don't get. Why anything here should satisfy DecoderRunnable? DecoderRunnable is a trait that has a run function that expects Arc<dyn LockableOption<dyn Decoder<T>>> and I'm passing rr which is exactly that.
Traits have to be implemented types so the compiler can figure out which implementation to run. In your example, DecoderRunnable is only implemented for FfmpegDecoder<T> and you're trying to call it on an Arc<dyn LockableOption<dyn Decoder<u8>>>, which has no implementation.
You can always specify which implementation needs to be called by using this syntax:
<FfmpegDecoder<u8> as DecoderRunnable::<u8>>::run(rr);
Although it doesn't seem like what you're trying to do . It's not clear what you're trying to abstract, since you also have the decoder deeply nested inside LockableArc<T>.
If you just want to add convenience methods to LockableArc<Box<dyn Decoder<u8>>>, you can add an impl block for Arc<dyn ...> , and make the run method take &self instead of Arc<dyn ...> as its first parameter.
I'm trying to store a FnMut in a struct:
struct OpenVPNSocket {
socket_send_callback: Option<Box<dyn FnMut(Vec<u8>) -> Result<(), ()>>>,
}
impl OpenVPNSocket {
fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()>>(&mut self, callback: Box<F>) {
self.socket_send_callback = Some(callback);
}
}
I get this error:
error[E0310]: the parameter type `F` may not live long enough
--> src/lib.rs:8:42
|
7 | fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()>>(&mut self, callback: Box<F>) {
| -- help: consider adding an explicit lifetime bound...: `F: 'static +`
8 | self.socket_send_callback = Some(callback);
| ^^^^^^^^ ...so that the type `F` will meet its required lifetime bounds
I understand lifetime as something to do with references. However I don't use references. I don't see why my struct cannot store a Box. A Box lives as long as it's used.
UPDATE:
I have this example:
use std::sync::Arc;
pub type OnConsume = Arc<dyn Fn() -> Option<u8> + Send + Sync>;
struct Test {
callback: OnConsume
}
impl Test {
fn set_on_consume(&mut self, f: OnConsume) {
self.callback = f;
}
}
which works. What is the difference from the previous one?
In Rust, values also have lifetimes. Take, for example, this struct:
struct RefWrapper<'a> {
some_ref: &'a u32
}
An instance of RefWrapper is not a reference, but contains a lifetime. Since you're moving the box into the struct, which could live for the duration of the program (the method makes no guarantees as to when the struct instance could be dropped), the function must live for the maximum lifetime, the static lifetime.
All trait objects have lifetimes, the default implicit lifetime for boxed trait objects is 'static so your struct's socket_send_callback actually has an implicit + 'static bound. Shown in context:
struct OpenVPNSocket {
socket_send_callback: Option<Box<dyn FnMut(Vec<u8>) -> Result<(), ()> + 'static>>,
}
Since the boxed trait object has to be bounded by a 'static lifetime when you write a function to set this field the value itself must have a 'static lifetime which is why the compiler suggests adding that explicit bound. Fixed example with added bound:
impl OpenVPNSocket {
// notice the added 'static bound for F
fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()> + 'static>(&mut self, callback: Box<F>) {
self.socket_send_callback = Some(callback);
}
}
With this change your code will compile. If you want to accept trait objects that aren't bounded by 'static lifetimes then you can do that by making your OpenVPNSocket generic over lifetimes. This alternative solution also compiles:
struct OpenVPNSocket<'a> {
socket_send_callback: Option<Box<dyn FnMut(Vec<u8>) -> Result<(), ()> + 'a>>,
}
impl<'a> OpenVPNSocket<'a> {
fn set_socket_send<F: FnMut(Vec<u8>) -> Result<(), ()> + 'a>(&mut self, callback: Box<F>) {
self.socket_send_callback = Some(callback);
}
}
The reason why this code works is because you define the type once and use it in multiple places, and in all places it has the implicit 'static bound. Desugared:
use std::sync::Arc;
pub type OnConsume = Arc<dyn Fn() -> Option<u8> + Send + Sync + 'static>;
struct Test {
callback: OnConsume
}
impl Test {
fn set_on_consume(&mut self, f: OnConsume) {
self.callback = f;
}
}
However you can do the exact same thing in your prior code as well:
type Callback = Box<dyn FnMut(Vec<u8>) -> Result<(), ()>>;
struct OpenVPNSocket {
socket_send_callback: Option<Callback>,
}
impl OpenVPNSocket {
fn set_socket_send(&mut self, callback: Callback) {
self.socket_send_callback = Some(callback);
}
}
The above also compiles, and the implicit 'static bound is still there.
I'm working on a network service that's intended to work with either a TcpStream or stdin/stdout. I get a compile error: the trait tokio::io::util::async_read_ext::AsyncReadExt cannot be made into an object. Currently my workaround is to use a wrapper enum:
enum ClientReader {
Stream(OwnedReadHalf),
Stdin(Stdin),
}
enum ClientWriter {
Stream(OwnedWriteHalf),
Stdout(Stdout),
}
This requires match blocks all over the place, which seems inelegant.
I made a simplified project to repro the issue:
Cargo.toml
[package]
name = "demo"
version = "0.1.0"
authors = ["test"]
edition = "2018"
[dependencies]
tokio = { version = "0.2", features = ["full"] }
src/main.rs
use tokio::io::AsyncReadExt;
struct Test {
test: Box<dyn AsyncReadExt>,
}
fn main () {}
This produces a similar error:
error[E0038]: the trait `tokio::io::AsyncReadExt` cannot be made into an object
--> src/main.rs:4:3
|
4 | test: Box<dyn AsyncReadExt>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `tokio::io::AsyncReadExt` cannot be made into an object
|
::: /home/???/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.22/src/io/util/async_read_ext.rs:162:12
|
162 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self>
| ---- the trait cannot be made into an object because method `read` references the `Self` type in its return type
...
280 | fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self>
| ---------- the trait cannot be made into an object because method `read_exact` references the `Self` type in its return type
I'm not sure how to proceed. I was considering a giant impl block for the enum wrapper, but that seems like more work than the match blocks. In an OO language there'd be a parent class or interface so I investigated the trait_enum crate to automate making a wrapper impl but had a lot of trouble getting that to work.
At the moment the only cleanup I'm sure will work is to move the workaround into a macro or function.
I'd appreciate any feedback on a better way to do this. :)
EDIT: per suggestion by user4815162342 I made the struct generic over type AsyncReadExt and this appears to work for my example. Will try on my larger project later.
use tokio::io::AsyncReadExt;
struct Test<T: AsyncReadExt> {
test: T,
}
async fn myfn<T: AsyncReadExt>(mut t: Test<T>) where T: std::marker::Unpin {
let mut v = Vec::<u8>::new();
t.test.read_buf(&mut v).await;
}
fn main () {}
To turn an AsyncRead into a trait object, you should use the type Pin<Box<dyn AsyncRead>>.
use std::pin::Pin;
use tokio::io::AsyncRead;
struct Test {
test: Pin<Box<dyn AsyncRead>>,
}
impl Test {
fn new<T: AsyncRead>(io: T) -> Self {
Self {
test: Box::pin(io),
}
}
}
The AsyncReadExt trait is an extension trait, and you should always use the AsyncRead trait when mentioning the type of an AsyncRead.