push on SelectAll Stream - rust

I would like to have a struct for handling some streams. All streams share the same Item. The logic is the following: I need to create a unique stream that contains all items that came from all other streams. I need also to add "new" stream to the "main" stream. I don't care from which stream the next item comes from.
For doing that, I see there's the select_all function that should do the logic described above.
pub struct WsPool {
merge: Arc<Mutex<SelectAll<Box<dyn Stream<Item=MyItem> + Send + 'static>>>>,
}
impl WsPool {
pub fn new() -> Self {
Self {
merge: Arc::new(Mutex::new(SelectAll::new())),
}
}
pub fn add(&self, s: Box<dyn Stream<Item = MyItem> + Send + 'static>) {
let mut merge = self.merge.lock().unwrap();
merge.push(s);
}
pub async fn process(&self) {
loop {
let mut merge = self.merge.lock().unwrap();
let item = merge.await.next();
}
}
}
But I receive these errors:
error[E0277]: `std::sync::MutexGuard<'_, futures::stream::SelectAll<Box<(dyn futures::Stream<Item = MyItem> + std::marker::Send + 'static)>>>` is not a future
--> src/ws_pool.rs:30:24
|
30 | let item = merge.await.next();
| ^^^^^^^^^^^ `std::sync::MutexGuard<'_, futures::stream::SelectAll<Box<(dyn futures::Stream<Item = MyItem> + std::marker::Send + 'static)>>>` is not a future
|
= help: the trait `futures::Future` is not implemented for `std::sync::MutexGuard<'_, futures::stream::SelectAll<Box<(dyn futures::Stream<Item = MyItem> + std::marker::Send + 'static)>>>`
note: required by `futures::Future::poll`
--> /home/allevo/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/future.rs:99:5
|
99 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: `(dyn futures::Stream<Item = MyItem> + std::marker::Send + 'static)` cannot be unpinned
--> src/ws_pool.rs:17:40
|
17 | merge: Arc::new(Mutex::new(SelectAll::new())),
| ^^^^^^^^^^^^^^^^ the trait `Unpin` is not implemented for `(dyn futures::Stream<Item = MyItem> + std::marker::Send + 'static)`
|
= note: consider using `Box::pin`
= note: required because of the requirements on the impl of `futures::Stream` for `Box<(dyn futures::Stream<Item = MyItem> + std::marker::Send + 'static)>`
note: required by `futures::stream::SelectAll::<St>::new`
--> /home/allevo/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.17/src/stream/select_all.rs:47:5
|
47 | pub fn new() -> Self {
| ^^^^^^^^^^^^^^^^^^^^
error[E0599]: the method `push` exists for struct `std::sync::MutexGuard<'_, futures::stream::SelectAll<Box<(dyn futures::Stream<Item = MyItem> + std::marker::Send + 'static)>>>`, but its trait bounds were not satisfied
--> src/ws_pool.rs:24:15
|
24 | merge.push(s);
| ^^^^ method cannot be called on `std::sync::MutexGuard<'_, futures::stream::SelectAll<Box<(dyn futures::Stream<Item = MyItem> + std::marker::Send + 'static)>>>` due to unsatisfied trait bounds
|
::: /home/allevo/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/boxed.rs:172:1
|
172 | / pub struct Box<
173 | | T: ?Sized,
174 | | #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
175 | | >(Unique<T>, A);
| |________________- doesn't satisfy `_: futures::Stream`
|
= note: the following trait bounds were not satisfied:
`Box<(dyn futures::Stream<Item = MyItem> + std::marker::Send + 'static)>: futures::Stream`
What am I doing wrong? Otherwise how can I store multiple streams and iterate over them?

Your first problem is a simple mixup. You want to await the next value, not the stream itself:
let item = merge.next().await;
The resulting errors are all because SelectAll<Box<dyn Stream + Send + 'static>> does not implement Stream. If you look at the impl Stream for SelectAll, it is constrained that the inner stream type implements Unpin.
You can fix this by adding it to the bounds:
use std::marker::Unpin;
// vvvvv
Arc<Mutex<SelectAll<Box<dyn Stream<Item=MyItem> + Unpin + Send + 'static>>>>
Or a better solution is to just pin the streams:
use std::pin::Pin;
// vvv
Arc<Mutex<SelectAll<Pin<Box<dyn Stream<Item=MyItem> + Send + 'static>>>>>
The difference is the latter can accept more Stream types. You just have to use Box::pin when adding them.
See it working on the Playground.

Related

Parsing Traces with Opaque Errors in Rust

How does one read and understand Rust traces with Opaque errors?
Example Error
For example, consider the following typically long error. How do you infer the problem from the error output?
error[E0599]: the method `next` exists for struct `Pin<&mut impl futures::Future<Output = AsyncStream<Result<u32, Box<dyn StdError>>, Opaque(DefId(0:51 ~ armadillo_rust[2000]::query_all::{opaque#0}::{opaque#0}), ['_#6r])>>>`, but its trait bounds were not satisfied
--> src/main.rs:198:31
|
198 | while let Some(x) = ticks.next().await {
| ^^^^ method cannot be called on `Pin<&mut impl futures::Future<Output = AsyncStream<Result<u32, Box<dyn StdError>>, Opaque(DefId(0:51 ~ armadillo_rust[2000]::query_all::{opaque#0}::{opaque#0}), ['_#6r])>>>` due to unsatisfied trait bounds
|
::: ../toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/pin.rs:408:1
|
408 | pub struct Pin<P> {
| -----------------
| |
| doesn't satisfy `_: StreamExt`
| doesn't satisfy `_: futures::Stream`
|
= note: the following trait bounds were not satisfied:
`Pin<&mut impl futures::Future<Output = AsyncStream<Result<u32, Box<dyn StdError>>, Opaque(DefId(0:51 ~ armadillo_rust[2000]::query_all::{opaque#0}::{opaque#0}), ['_#6r])>>>: futures::Stream`
which is required by `Pin<&mut impl futures::Future<Output = AsyncStream<Result<u32, Box<dyn StdError>>, Opaque(DefId(0:51 ~ armadillo_rust[2000]::query_all::{opaque#0}::{opaque#0}), ['_#6r])>>>: StreamExt`
`impl futures::Future<Output = AsyncStream<Result<u32, Box<dyn StdError>>, Opaque(DefId(0:51 ~ armadillo_rust[2000]::query_all::{opaque#0}::{opaque#0}), ['_#6r])>>: futures::Stream`
which is required by `impl futures::Future<Output = AsyncStream<Result<u32, Box<dyn StdError>>, Opaque(DefId(0:51 ~ armadillo_rust[2000]::query_all::{opaque#0}::{opaque#0}), ['_#6r])>>: StreamExt`
Minimal, Working Example:
use futures::StreamExt;
use async_stream::AsyncStream;
async fn query_all<'a>(date: &'a str) -> AsyncStream<Result<u32, Box<dyn std::error::Error + 'a>>, impl futures::Future + 'a> {
todo!();
}
#[tokio::main]
async fn main() {
let ticks = query_all("2022-06-14");
futures::pin_mut!(ticks);
while let Some(x) = ticks.next().await {
println!("{:?}", x);
}
}
Naive Solution
The error seems to be saying two things:
A pinned stream doesn't satisfy necessary bounds
The return type of query_all fails to implement certain traits.
However, confusingly, the exact same types work in this minimal example:
#[tokio::main]
async fn main() {
let ticks = stream! {
yield Ok(0);
yield Err("");
};
futures::pin_mut!(ticks);
while let Some(x) = ticks.next().await {
println!("{:?}", x);
}
}
How am I misreading this and what is Rust really trying to say?

Wrap an async function

The AWS Lambda Runtime for rust requires handlers be like async fn handler(event: Value, cx: Context) -> Result<Value, Error>. It handles the errors you return internally, but I don't like how it logs them. I want to create a function that I can pass a handler function and it returns me a wrapped version, which just calls the original, inspects its results, maybe logs, and returns the results. This has proved ... difficult.
I've managed to do it with a macro, but I can't work out how to make a real function do it. Any help would be much appreciated. Code:
use anyhow::{Result};
use lambda_runtime::{handler_fn, Context, Error};
use serde_json::{Value};
// handler
async fn handler(event: Value, cx: Context) -> Result<Value> {
Ok(())
}
// non-generic version (works)
async fn wrapper(event: Value, cx: Context) -> Result<Value> {
let res = handler(event, cx).await;
if let Err(e) = &res {
log::error!("got error from handler: {:?}", &e);
}
res
}
// macro (works)
macro_rules! wrap_handler {
($f:ident) => {{
|e: Value, c: Context| async {
let res = $f(e, c).await;
if let Err(e) = &res {
log::error!("got error from handler: {:?}", &e);
}
res
}
}};
}
// attempt at generic wrapper-creator fn
// should take the function to wrap, and return a Fn wrapping it
use std::future::Future;
use std::pin::Pin;
pub fn make_wrapper<F, FO>(
f: F,
) -> Box<dyn Fn(Value, Context) -> Pin<Box<dyn Future<Output = Result<Value>> + 'static>> + 'static>
where
F: Fn(Value, Context) -> FO + 'static,
FO: Future<Output = Result<Value>> + 'static,
{
use std::sync::Arc;
let af = Arc::new(f);
Box::new(move |v: Value, c: Context| {
let my_f = af.clone();
Box::pin(async move {
//
my_f(v, c).await
})
})
}
// main
#[tokio::main]
async fn main() -> Result<(), Error> {
// use the non-generic version
let func = handler_fn(wrapper);
// OR
// use the macro
let func = handler_fn(wrap_handler!(handler));
// OR
// use the generic wrapper function
let func = handler_fn(make_wrapper(handler));
lambda_runtime::run(func).await?;
Ok(())
}
The error with my latest attempt (the fn in the code above) is:
error[E0277]: `dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>` cannot be sent between threads safely
--> src/bin/classify-partition.rs:126:25
|
126 | lambda_runtime::run(func).await?;
| ^^^^ `dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>` cannot be sent between threads safely
|
::: /home/miles/.cargo/registry/src/github.com-1ecc6299db9ec823/lambda_runtime-0.3.0/src/lib.rs:294:24
|
294 | F: Handler<A, B> + Send + Sync + 'static,
| ---- required by this bound in `run`
|
= help: the trait `Send` is not implemented for `dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>`
= note: required because of the requirements on the impl of `Send` for `Unique<dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>>`
= note: required because it appears within the type `Box<dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>>`
= note: required because it appears within the type `HandlerFn<Box<dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>>>`
error[E0277]: `dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>` cannot be shared between threads safely
--> src/bin/classify-partition.rs:126:25
|
126 | lambda_runtime::run(func).await?;
| ^^^^ `dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>` cannot be shared between threads safely
|
::: /home/miles/.cargo/registry/src/github.com-1ecc6299db9ec823/lambda_runtime-0.3.0/src/lib.rs:294:31
|
294 | F: Handler<A, B> + Send + Sync + 'static,
| ---- required by this bound in `run`
|
= help: the trait `Sync` is not implemented for `dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>`
= note: required because of the requirements on the impl of `Sync` for `Unique<dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>>`
= note: required because it appears within the type `Box<dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>>`
= note: required because it appears within the type `HandlerFn<Box<dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>>>`
error[E0277]: `(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)` cannot be sent between threads safely
--> src/bin/classify-partition.rs:126:25
|
126 | lambda_runtime::run(func).await?;
| ^^^^ `(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)` cannot be sent between threads safely
|
= help: the trait `Send` is not implemented for `(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)`
= note: required because of the requirements on the impl of `Send` for `Unique<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>`
= note: required because it appears within the type `Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>`
= note: required because it appears within the type `Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>`
= note: required because of the requirements on the impl of `Handler<Value, Value>` for `HandlerFn<Box<dyn Fn(Value, lambda_runtime::Context) -> Pin<Box<(dyn Future<Output = Result<Value, anyhow::Error>> + 'static)>>>>`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`.
error: could not compile `enrichment-service-functions`
To learn more, run the command again with --verbose.

How to return a mapped future in an actix_web FromRequest trait implementation? [duplicate]

This question already has answers here:
How do I return an instance of a trait from a method?
(4 answers)
Is it possible to use `impl Trait` as a function's return type in a trait definition?
(4 answers)
Why can impl trait not be used to return multiple / conditional types?
(2 answers)
Closed 3 years ago.
I have the following implementation of the actix_web FromRequest trait:
impl ::actix_web::FromRequest for Box<dyn SessionRepository> {
type Error = ::actix_web::Error;
type Future =
::futures::future::MapOk<::futures::future::Ready<Result<Self, Self::Error>>, ???>;
type Config = ();
fn from_request(
req: &::actix_web::HttpRequest,
payload: &mut actix_web::dev::Payload,
) -> Self::Future {
RealSessionRepository::from_request(&req, payload).map_ok(|dep| Box::new(dep))
}
}
As the return type is an FnOnce, I cannot figure out how to actually set the return type. According to the error message it should be FnOnce<(std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)>,)>, however that tells me that fnOnce is not valid because it's not a known size at compile time.
RealSessionRepository::from_request returns a future, also being an implementation of the FromRequest trait.
Inserting the FnOnce definition like this:
impl ::actix_web::FromRequest for Box<dyn SessionRepository> {
type Error = ::actix_web::Error;
type Future = ::futures::future::MapOk<
::futures::future::Ready<Result<Self, Self::Error>>,
FnOnce(RealSessionRepository) -> Box<(dyn SessionRepository + 'static)>,
>;
type Config = ();
fn from_request(
req: &::actix_web::HttpRequest,
payload: &mut actix_web::dev::Payload,
) -> Self::Future {
RealSessionRepository::from_request(&req, payload).map_ok(|dep| Box::new(dep))
}
}
gives the following set of error messages:
error[E0277]: the size for values of type `(dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)` cannot be known at compilation time
--> api-server\src\db\sessions.rs:32:6
|
32 | impl ::actix_web::FromRequest for Box<dyn SessionRepository> {
| ^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: required because of the requirements on the impl of `core::future::future::Future` for `futures_util::future::try_future::map_ok::MapOk<futures_util::future::ready::Ready<std::result::Result<std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)>, actix_web::Error>>, (dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)>`
error[E0277]: expected a `std::ops::FnOnce<(std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)>,)>` closure, found `(dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)`
--> api-server\src\db\sessions.rs:32:6
|
32 | impl ::actix_web::FromRequest for Box<dyn SessionRepository> {
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce<(std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)>,)>` closure, found `(dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)`
|
= help: the trait `std::ops::FnOnce<(std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)>,)>` is not implemented for `(dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)`
= note: required because of the requirements on the impl of `core::future::future::Future` for `futures_util::future::try_future::map_ok::MapOk<futures_util::future::ready::Ready<std::result::Result<std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)>, actix_web::Error>>, (dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)>`
error[E0277]: the size for values of type `(dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)` cannot be known at compilation time
--> api-server\src\db\sessions.rs:34:5
|
34 | / type Future = ::futures::future::MapOk<
35 | | ::futures::future::Ready<Result<Self, Self::Error>>,
36 | | FnOnce(RealSessionRepository) -> Box<(dyn SessionRepository + 'static)>,
37 | | >;
| |______^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: required by `futures_util::future::try_future::map_ok::MapOk`
error[E0277]: the size for values of type `(dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)` cannot be known at compilation time
--> api-server\src\db\sessions.rs:40:5
|
40 | / fn from_request(
41 | | req: &::actix_web::HttpRequest,
42 | | payload: &mut actix_web::dev::Payload,
43 | | ) -> Self::Future {
44 | | RealSessionRepository::from_request(&req, payload).map_ok(|dep| Box::new(dep))
45 | | }
| |_____^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::FnOnce(db::sessions::RealSessionRepository) -> std::boxed::Box<(dyn db::sessions::SessionRepository + 'static)> + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: required by `futures_util::future::try_future::map_ok::MapOk`
MRE is available at: https://github.com/zlepper/actix_web_mre since it requires actix, which is not available on the rust playground.
This is specially about how to handle this when i want a dynamic trait reference, in an associated type, which I do not own, and thus can't do a lot about changing. More specifically, how would i do it with an FnOnce, where the actual implementation is only generated at compile time (I would think).
I know I can shorten the type definitions slightly so they are not fully qualified, however this is something I would like to generate using a macro, so as far as I know it is better to use the full types.
Is there a better way to type out the return future? The current definition is quite hairy..
FnOnce is a trait and as such doesn't have a known size at compile time. However we can wrap it in a Box, which has a known size, to make it compile:
impl ::actix_web::FromRequest for Box<dyn SessionRepository> {
type Error = ::actix_web::Error;
type Future = ::futures::future::MapOk<
::futures::future::Ready<Result<RealSessionRepository, Self::Error>>,
Box<dyn FnOnce(RealSessionRepository) -> Box<(dyn SessionRepository)>>,
>;
type Config = ();
fn from_request(
req: &::actix_web::HttpRequest,
payload: &mut actix_web::dev::Payload,
) -> Self::Future {
RealSessionRepository::from_request(&req, payload).map_ok(Box::new(|dep| Box::new(dep)))
}
}

How can I put an async function into a map in Rust?

I cannot handle async functions when writing an async router for hyper.
This code:
use std::collections::HashMap;
use std::future::Future;
type BoxedResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
type CalcFn = Box<dyn Fn(i32, i32) -> dyn Future<Output = BoxedResult<i32>>>;
async fn add(a: i32, b: i32) -> BoxedResult<i32> {
Ok(a + b)
}
async fn sub(a: i32, b: i32) -> BoxedResult<i32> {
Ok(a - b)
}
fn main() {
let mut map: HashMap<&str, CalcFn> = Default::default();
map.insert("add", Box::new(add));
map.insert("sub", Box::new(sub));
println!("map size: {}", map.len());
}
Generates the following compiler error:
error[E0271]: type mismatch resolving `<fn(i32, i32) -> impl std::future::Future {add} as std::ops::FnOnce<(i32, i32)>>::Output == dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`
--> src/main.rs:17:23
|
17 | map.insert("add", Box::new(add));
| ^^^^^^^^^^^^^ expected opaque type, found trait std::future::Future
|
= note: expected type `impl std::future::Future`
found type `dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`
= note: required for the cast to the object type `dyn std::ops::Fn(i32, i32) -> dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`
error[E0271]: type mismatch resolving `<fn(i32, i32) -> impl std::future::Future {sub} as std::ops::FnOnce<(i32, i32)>>::Output == dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`
--> src/main.rs:18:23
|
18 | map.insert("sub", Box::new(sub));
| ^^^^^^^^^^^^^ expected opaque type, found trait std::future::Future
|
= note: expected type `impl std::future::Future`
found type `dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`
= note: required for the cast to the object type `dyn std::ops::Fn(i32, i32) -> dyn std::future::Future<Output = std::result::Result<i32, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>>>`
It seems there is a conflict between impl Future and dyn Future, but I have no idea how to handle it.
This happens because impl Future is a concrete unique type while dyn Future is an abstract type. HashMap expects the abstract type since it can only hold instances of a single type.
If we can box the return type of the async functions, we will able to add these futures into a HashMap.
First we need to change the type of CalcFn:
type CalcFn = Box<dyn Fn(i32, i32) -> Pin<Box<dyn Future<Output = i32>>>>;
Then this can do the trick:
let mut map: HashMap<&str, CalcFn> = Default::default();
map.insert("add", Box::new(|a, b| Box::pin(add(a, b))));
map.insert("sub", Box::new(|a, b| Box::pin(sub(a, b))));
println!("map size: {}", map.len());
//map.get("add").unwrap()(2, 3).await
This complete example
simplified Future's Item type, using an i32 instead of a Result. Please also check the full code for your case.
You can also use types from the futures crate like LocalBoxFuture and BoxFuture created by the FutureExt::boxed and FutureExt::boxed_local methods respectively:
use futures::future::{FutureExt, LocalBoxFuture}; // 0.3.5
use std::collections::HashMap;
type BoxedResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
type CalcFn = Box<dyn Fn(i32, i32) -> LocalBoxFuture<'static, BoxedResult<i32>>>;
async fn add(a: i32, b: i32) -> BoxedResult<i32> {
Ok(a + b)
}
async fn sub(a: i32, b: i32) -> BoxedResult<i32> {
Ok(a - b)
}
async fn example() {
let mut map: HashMap<&str, CalcFn> = Default::default();
map.insert("add", Box::new(|a, b| add(a, b).boxed()));
map.insert("sub", Box::new(|a, b| sub(a, b).boxed()));
println!("map size: {}", map.len());
//map.get("add").unwrap()(2, 3).await
}

How do I treat generics?

I would like to calculate a factorial using generics, but I get an error in main.
My full code:
pub trait Body {
fn new() -> Self;
fn fact(&self, x: usize) -> usize {
match x {
1 => 1,
_ => x * self.fact(x - 1),
}
}
}
#[derive(Clone, Debug)]
pub struct RecursiveCall<T: Body> {
level: usize,
indicator: String,
n_repeat: usize,
body: T,
}
impl<T> RecursiveCall<T>
where T: Body
{
fn new(n_repeat: usize) -> RecursiveCall<T> {
RecursiveCall {
level: 0,
indicator: "- ".to_string(),
n_repeat: n_repeat,
body: <T as Body>::new(),
}
}
fn pre_trace(&self, fname: &str, arg: &usize) {
let args: String = arg.to_string();
println!("{}",
(vec![self.indicator.as_str(); self.level]).join("") +
self.level.to_string().as_str() + ":" + fname + "(" +
args.as_str() + ")");
}
fn post_trace(&self, fname: &str, arg: &usize, ret: &usize) {
println!("{}",
(vec![self.indicator.as_str(); self.level]).join("") +
self.level.to_string().as_str() + ":" + fname + "=" +
ret.to_string().as_str());
}
fn print_trace(&mut self) {
&self.pre_trace("fact", &self.n_repeat);
self.level += 1;
let ret = &self.body.fact(self.n_repeat);
self.level -= 1;
&self.post_trace("fact", &self.n_repeat, ret);
println!("Difference={}", &ret.to_string().as_str());
}
}
type B = Body;
fn main() {
let t = RecursiveCall::<B>::new();
}
This error occurs in main():
error: no associated item named `new` found for type `RecursiveCall<Body + 'static>` in the current scope
--> src/main.rs:61:13
|
61 | let t = RecursiveCall::<B>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the method `new` exists but the following trait bounds were not satisfied: `Body : std::marker::Sized`, `Body : Body`
= help: items from traits can only be used if the trait is implemented and in scope; the following traits define an item `new`, perhaps you need to implement one of them:
= help: candidate #1: `Body`
= help: candidate #2: `std::sys_common::thread_info::NewThread`
= help: candidate #3: `std::iter::ZipImpl`
error[E0277]: the trait bound `Body + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:61:13
|
61 | let t = RecursiveCall::<B>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Sized` is not implemented for `Body + 'static`
|
= note: `Body + 'static` does not have a constant size known at compile-time
= note: required by `RecursiveCall`
error[E0277]: the trait bound `Body + 'static: Body` is not satisfied
--> src/main.rs:61:13
|
61 | let t = RecursiveCall::<B>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Body` is not implemented for `Body + 'static`
|
= note: required by `RecursiveCall`
error[E0038]: the trait `Body` cannot be made into an object
--> src/main.rs:61:13
|
61 | let t = RecursiveCall::<B>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Body` cannot be made into an object
|
= note: method `new` has no receiver
The key to the answer is in this note from the compiler:
note: the method new exists but the following trait bounds were not satisfied: Body : std::marker::Sized, Body : Body
You've defined B as a type alias for Body, so the names B and Body both mean the same thing in your program (at least in this module).
When you define a trait, the compiler also defines a type with the same name. However, that type cannot be instantiated directly (unlike a class in C++/C#/Java/etc.). Yet, that's exactly what you're trying to do!
The trait bound Body : std::marker::Sized is not satisfied because Body, being the type defined by the compiler corresponding to the trait of the same name, is an unsized type. Unsized types can only be used in pointers and references (e.g. &Body, Box<Body>, etc.).
The trait bound Body : Body is not satisfied because your trait is not object-safe. It's not object-safe because the method new doesn't have a self parameter (this is what the compiler means in the last note: method `new` has no receiver).
Normally, you would define a struct or an enum and implement the trait for that type, and then use that type when instantiating RecursiveCall. Try replacing type B = Body; with the following:
struct B;
impl Body for B {
fn new() -> B {
B
}
}

Resources