lifetime may not live long enough when passing &mut inside async closure? - rust

I want to write function transactional which handle logic related to transactions committing
fn get_client() -> Client {
return Client{};
}
struct Client {}
impl Client {
pub async fn commit(&mut self) -> Result<(), Error> {
return Ok(());
}
pub async fn find_and_update(&mut self) -> Vec<u64> {
return vec![];
}
}
pub async fn transactional<F, Fut, R>(action: F) -> Result<R, Error>
where
F: Fn(&mut Client) -> Fut,
Fut: Future<Output = R>
{
let mut client = get_client();
loop {
let action_result = action(&mut client).await;
if let Err(err) = client.commit().await {
continue;
}
return Ok(action_result);
}
}
pub async fn make_request() -> Vec<u64> {
return transactional(
async move |session| session.find_and_update().await
).await.unwrap();
}
#[tokio::main]
async fn main() -> Result<(), io::Error>{
let r = make_request().await;
return Ok(())
}
but i get following error
| async move |session| session.find_and_update().await
| ^^^^^^^^^^^^--------
| | | |
| | | return type of closure `impl futures::Future<Output = Vec<u64>>` contains a lifetime `'2`
| | has type `&'1 mut Client`
| returning this value requires that `'1` must outlive `'2`
is it possible to specify that &Client outlives Future and both lives less than loop iteration?
is it possible to fix this wihtout using pointers?
cargo --version
cargo 1.64.0-nightly (a5e08c470 2022-06-23)

At first I rewrote your code to not use unstable async closures. Then you see, that borrowing Client in an async block |session| async move {..} is only possible for 'static and you don't have it. So you need to give it an owned value. In my case I pass ownership to the async block and return it in the result. Not sure if this is a good design, but it works.
use std::future::Future;
use tokio; // 1.19.2
fn get_client() -> Client {
return Client{};
}
pub struct Client {}
impl Client {
pub async fn commit(&mut self) -> Result<(), Error> {
return Ok(());
}
pub async fn find_and_update(&mut self) -> Vec<u64> {
return vec![];
}
}
pub async fn transactional<F, Fut, R>(action: F) -> Result<R, Error>
where
F: Fn(Client) -> Fut,
Fut: Future<Output = (R, Client)>
{
let mut client = get_client();
loop {
let (action_result, c) = action(client).await;
client = c;
if let Err(err) = client.commit().await {
continue;
}
return Ok(action_result);
}
}
pub async fn make_request() -> Vec<u64> {
return transactional(|mut session| async move { // move `client` into async, not borrow
let r = session.find_and_update().await;
(r, session)
}).await.unwrap();
}
#[tokio::main]
async fn main() -> Result<(), std::io::Error>{
let r = make_request().await;
return Ok(())
}
#[derive(Debug)]
pub struct Error;

Related

Rust Generic Function Attempt for Async Reads

My goal is to reduce the following read_stream_***() functions into a generic fuction that can be passed different streams.
use async_std::net::TcpStream;
use async_std::{ task };
use async_std::io::{ stdin, BufReader, Stdin };
use async_std:: { prelude::* };
use futures::{select, FutureExt, AsyncRead };
pub async fn read_stream_stdin(streem:Stdin) -> Result<(), std::io::Error>
{
let mut lines_from_stream = BufReader::new(streem).lines().fuse();
loop {
select! {
line = lines_from_stream.next().fuse() => match line {
Some(line) => {
println!("{:?}",line?);
}
None => break,
}
}
}
Ok(())
}
pub async fn read_stream_tcp(streem:TcpStream) -> Result<(), std::io::Error>
{
let mut lines_from_stream = BufReader::new(streem).lines().fuse();
loop {
select! {
line = lines_from_stream.next().fuse() => match line {
Some(line) => {
println!("{:?}",line?);
}
None => break,
}
}
}
Ok(())
}
pub async fn connect_tcp_server(host_port:&str) -> Result<(), std::io::Error>
{
let streem = TcpStream::connect(host_port).await;
let _result = task::block_on(read_stream_tcp(streem?));
Ok(())
}
fn main() -> Result<(), std::io::Error> {
task::spawn( connect_tcp_server("127.0.0.1:8081") );
task::block_on(read_stream_stdin(stdin()))
}
The Generic Attempt:
pub async fn read_stream<T>(streem:T) -> Result<(), std::io::Error>
{
let mut lines_from_stream = BufReader::new(streem).lines().fuse();
loop {
select! {
line = lines_from_stream.next().fuse() => match line {
Some(line) => {
println!("{:?}",line?);
}
None => break,
}
}
}
Ok(())
}
The Cargo.toml
[package]
name = "gen_func"
version = "0.1.0"
edition = "2021"
[dependencies]
async-std = "1.9.0"
futures = "0.3.21"
I attempted <T: async_std::io::Read> but fuse() and lines() are not implemented. and AsyncRead is not found in async_std::io . I found AsyncRead in futures crate but again fuse() and lines() were not implemented. I am not set on the read pattern. I am new to Rust and trying to build my source library to solve future programming tasks.
First, as pointed out by kmdreko, the logic of your function(s) can be greatly simplified (at least based on the information given):
pub async fn read_stream_tcp(stream: TcpStream) -> Result<(), std::io::Error> {
let mut lines = BufReader::new(stream).lines();
while let Some(line) = lines.next().await {
println!("{line:?}");
}
}
Ok(())
Then, to figure out how to make this generic, you can just let the compiler tell you what it needs:
pub async fn read_stream<T>(stream: T) -> Result<(), std::io::Error>
{
let mut lines = BufReader::new(stream).lines();
while let Some(line) = lines.next().await {
println!("{line:?}");
}
Ok(())
}
Notice the lack of where clauses or other constraints on T. The compiler will now complain:
error[E0277]: the trait bound `T: async_std::io::Read` is not satisfied --> src/main.rs:15:36 |
15 | let mut lines = BufReader::new(stream).lines();
| -------------- ^^^^^^ the trait `async_std::io::Read` is not implemented for `T`
| |
| required by a bound introduced by this call
|
note: required by a bound in `async_std::io::BufReader::<R>::new`
--> /home/lucas/.cargo/registry/src/github.com-1ecc6299db9ec823/async-std-1.12.0/src/io/buf_reader.rs:55:9
|
55 | impl<R: io::Read> BufReader<R> {
| ^^^^^^^^ required by this bound in `async_std::io::BufReader::<R>::new`
help: consider restricting type parameter `T`
|
13 | pub async fn read_stream<T: async_std::io::Read>(stream: T) -> Result<(), std::io::Error>
| +++++++++++++++++++++
Applying the compiler's suggestions (the above will result in a follow-up error) yields a full where clause of T: async_std::io::Read + std::marker::Unpin:
pub async fn read_stream<T>(stream: T) -> Result<(), std::io::Error>
where
T: Read + std::marker::Unpin,
{
let mut lines = BufReader::new(stream).lines();
while let Some(line) = lines.next().await {
println!("{line:?}");
}
Ok(())
}
async fn try_it() {
// These will now compile just fine
read_stream(async_std::io::stdin()).await.unwrap();
read_stream(TcpStream::connect("127.0.0.1:8080").await.unwrap()).await.unwrap();
}
I attempted <T: async_std::io::Read> but fuse() and lines() are not implemented
This suggests that you tried replacing BufReader::new(stream) at the same time. You can do that, but you need to tell the compiler that you need something that implements the lines() method. Either make the parameter a fixed type BufReader<T> or make the where clause T: async_std::io::BufRead + std::marker::Unpin for a generic type.

How to fire async callback in rust

I'm trying to implement a StateMachine in Rust, but I encountered some problems while trying to fire the callback of StateMachine in a spawn thread.
Here is my StateMachine struct. The state is a generic T because I want to use it in many different scenerios, and I use a Vec to store all the callbacks those registered into this StateMachine.
At the very begining, I didn't use the lifetime 'a, but it will run into some lifetime problems, so I add the lifetime 'a by this suggestion: Idiomatic callbacks in Rust
pub struct StateMachine<'a, T> where T:Clone+Eq+'a {
state: RwLock<T>,
listeners2: Vec<Arc<Mutex<ListenerCallback<'a, T>>>>,
}
pub type ListenerCallback<'a, T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'a ;
When the state is changed, the StateMachine will fire all the callbacks, as follows.
pub async fn try_set(&mut self, new_state:T) -> Result<()> {
if (block_on(self.state.read()).deref().eq(&new_state)) {
return Ok(())
}
// todo change the state
// fire every listener in spawn
let mut fire_results = vec![];
for listener in &mut self.listeners2 {
let state = new_state.clone();
let fire_listener = listener.clone();
fire_results.push(tokio::spawn(async move {
let mut guard = fire_listener.lock().unwrap();
guard.deref_mut()(state);
}));
}
// if fire result return Err, return it
for fire_result in fire_results {
fire_result.await?;
}
Ok(())
}
But it will cause a compilation error.
error[E0521]: borrowed data escapes outside of associated function
--> src/taf/taf-core/src/execution/state_machine.rs:54:33
|
15 | impl<'a,T> StateMachine<'a,T> where T:Clone+Eq+Send {
| -- lifetime `'a` defined here
...
34 | pub async fn try_set(&mut self, new_state:T) -> Result<()> {
| --------- `self` is a reference that is only valid in the associated function body
...
54 | let fire_listener = listener.clone();
| ^^^^^^^^^^^^^^^^
| |
| `self` escapes the associated function body here
| argument requires that `'a` must outlive `'static`
##########################################################
The full code is coupled with a lot of business logic, so I rewrite 2 demos as follows, the problems is the same. The first demo fire callback synchronously and it works, the second demo try to fire callback asynchronously, it encounter the same problem: self escapes the associated function body here.
First demo(it works):
use std::alloc::alloc;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex, RwLock};
use anyhow::Result;
use dashmap::DashMap;
struct StateMachine<'a,T> where T:Clone+Eq+'a {
state: T,
listeners: Vec<Box<Callback<'a, T>>>,
}
type Callback<'a, T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'a;
impl<'a, T> StateMachine<'a,T> where T:Clone+Eq+'a {
pub fn new(init_state: T) -> Self {
StateMachine {
state: init_state,
listeners: vec![]
}
}
pub fn add_listener(&mut self, listener: Box<Callback<'a, T>>) -> Result<()> {
self.listeners.push(listener);
Ok(())
}
pub fn set(&mut self, new_state: T) -> Result<()> {
self.state = new_state.clone();
for listener in &mut self.listeners {
listener(new_state.clone());
}
Ok(())
}
}
#[derive(Clone, Eq, PartialEq, Hash)]
enum ExeState {
Waiting,
Running,
Finished,
Failed,
}
struct Execution<'a> {
exec_id: String,
pub state_machine: StateMachine<'a, ExeState>,
}
struct ExecManager<'a> {
all_jobs: Arc<RwLock<DashMap<String, Execution<'a>>>>,
finished_jobs: Arc<RwLock<Vec<String>>>,
}
impl<'a> ExecManager<'a> {
pub fn new() -> Self {
ExecManager {
all_jobs: Arc::new(RwLock::new(DashMap::new())),
finished_jobs: Arc::new(RwLock::new(vec![]))
}
}
fn add_job(&mut self, job_id: String) {
let mut execution = Execution {
exec_id: job_id.clone(),
state_machine: StateMachine::new(ExeState::Waiting)
};
// add listener
let callback_finished_jobs = self.finished_jobs.clone();
let callback_job_id = job_id.clone();
execution.state_machine.add_listener( Box::new(move |new_state| {
println!("listener fired!, job_id {}", callback_job_id.clone());
if new_state == ExeState::Finished || new_state == ExeState::Failed {
let mut guard = callback_finished_jobs.write().unwrap();
guard.deref_mut().push(callback_job_id.clone());
}
Ok(())
}));
let mut guard = self.all_jobs.write().unwrap();
guard.deref_mut().insert(job_id, execution);
}
fn mock_exec(&mut self, job_id: String) {
let mut guard = self.all_jobs.write().unwrap();
let mut exec = guard.deref_mut().get_mut(&job_id).unwrap();
exec.state_machine.set(ExeState::Finished);
}
}
#[test]
fn test() {
let mut manager = ExecManager::new();
manager.add_job(String::from("job_id1"));
manager.add_job(String::from("job_id2"));
manager.mock_exec(String::from("job_id1"));
manager.mock_exec(String::from("job_id2"));
}
Second demo:
use std::alloc::alloc;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex, RwLock};
use anyhow::Result;
use dashmap::DashMap;
use petgraph::algo::astar;
struct StateMachine<'a,T> where T:Clone+Eq+Send+'a {
state: T,
listeners: Vec<Arc<Mutex<Box<Callback<'a, T>>>>>,
}
type Callback<'a, T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'a;
impl<'a, T> StateMachine<'a,T> where T:Clone+Eq+Send+'a {
pub fn new(init_state: T) -> Self {
StateMachine {
state: init_state,
listeners: vec![]
}
}
pub fn add_listener(&mut self, listener: Box<Callback<'a, T>>) -> Result<()> {
self.listeners.push(Arc::new(Mutex::new(listener)));
Ok(())
}
pub fn set(&mut self, new_state: T) -> Result<()> {
self.state = new_state.clone();
for listener in &mut self.listeners {
let spawn_listener = listener.clone();
tokio::spawn(async move {
let mut guard = spawn_listener.lock().unwrap();
guard.deref_mut()(new_state.clone());
});
}
Ok(())
}
}
#[derive(Clone, Eq, PartialEq, Hash)]
enum ExeState {
Waiting,
Running,
Finished,
Failed,
}
struct Execution<'a> {
exec_id: String,
pub state_machine: StateMachine<'a, ExeState>,
}
struct ExecManager<'a> {
all_jobs: Arc<RwLock<DashMap<String, Execution<'a>>>>,
finished_jobs: Arc<RwLock<Vec<String>>>,
}
impl<'a> ExecManager<'a> {
pub fn new() -> Self {
ExecManager {
all_jobs: Arc::new(RwLock::new(DashMap::new())),
finished_jobs: Arc::new(RwLock::new(vec![]))
}
}
fn add_job(&mut self, job_id: String) {
let mut execution = Execution {
exec_id: job_id.clone(),
state_machine: StateMachine::new(ExeState::Waiting)
};
// add listener
let callback_finished_jobs = self.finished_jobs.clone();
let callback_job_id = job_id.clone();
execution.state_machine.add_listener( Box::new(move |new_state| {
println!("listener fired!, job_id {}", callback_job_id.clone());
if new_state == ExeState::Finished || new_state == ExeState::Failed {
let mut guard = callback_finished_jobs.write().unwrap();
guard.deref_mut().push(callback_job_id.clone());
}
Ok(())
}));
let mut guard = self.all_jobs.write().unwrap();
guard.deref_mut().insert(job_id, execution);
}
fn mock_exec(&mut self, job_id: String) {
let mut guard = self.all_jobs.write().unwrap();
let mut exec = guard.deref_mut().get_mut(&job_id).unwrap();
exec.state_machine.set(ExeState::Finished);
}
}
#[test]
fn test() {
let mut manager = ExecManager::new();
manager.add_job(String::from("job_id1"));
manager.add_job(String::from("job_id2"));
manager.mock_exec(String::from("job_id1"));
manager.mock_exec(String::from("job_id2"));
}
Compile error of second demo:
error[E0521]: borrowed data escapes outside of associated function
--> generic/src/callback2.rs:34:34
|
15 | impl<'a, T> StateMachine<'a,T> where T:Clone+Eq+Send+'a {
| -- lifetime `'a` defined here
...
29 | pub fn set(&mut self, new_state: T) -> Result<()> {
| --------- `self` is a reference that is only valid in the associated function body
...
34 | let spawn_listener = listener.clone();
| ^^^^^^^^^^^^^^^^
| |
| `self` escapes the associated function body here
| argument requires that `'a` must outlive `'static`
|
= note: requirement occurs because of the type `std::sync::Mutex<Box<dyn FnMut(T) -> Result<(), anyhow::Error> + Send + Sync>>`, which makes the generic argument `Box<dyn FnMut(T) -> Result<(), anyhow::Error> + Send + Sync>` invariant
= note: the struct `std::sync::Mutex<T>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
Tasks spawned with tokio::spawn() cannot use borrowed data (here, the data with lifetime 'a, whatever it may be). This is because there is not currently (and likely will never be) any way to guarantee that the borrowed data reliably outlives the spawned task.
You have two choices:
Fire the notifications without spawning. You can put the notification futures into a FuturesUnordered to run them all concurrently, but they will still all have to finish before try_set() does.
Remove the lifetime parameter; stop allowing callbacks that borrow data. Put 'static on your dyn types where necessary. Change the users of the StateMachine so they do not try to use borrowed data but use Arc instead, if necessary.
pub struct StateMachine<T> where T: Clone + Eq + 'static {
state: RwLock<T>,
listeners2: Vec<Arc<Mutex<ListenerCallback<T>>>>,
}
pub type ListenerCallback<T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'static;

How to downcast mutable structs not as references

I have this trait and implementation:
#[async_trait]
pub trait AsyncKeyProvider {
async fn get_key_async(&mut self, key_id: &str) -> Result<Option<Jwk>, ()>;
fn as_any(&self) -> &dyn Any;
}
#[async_trait]
impl AsyncKeyProvider for GoogleKeyProvider {
async fn get_key_async(&mut self, key_id: &str) -> Result<Option<Jwk>, ()> {
{...}
}
fn as_any(&self) -> &dyn Any {
self
}
}
In order to pass it into my handler in actix-web, I'm passing through a GoogleKeyProvider like this:
let key_provider = web::Data::from(Arc::new(GoogleKeyProvider::default()));
let server = HttpServer::new(move || {
App::new()
.app_data(key_provider.clone())
.route("/validate", web::post().to(validate))
})
With the handler doing this:
pub async fn validate(jwt_body: web::Json<JwtBody>, provider: web::Data<Box<dyn AsyncKeyProvider>>) -> impl Responder {
let provider_object: &GoogleKeyProvider = provider.as_any().downcast_ref::<GoogleKeyProvider>().expect("Wasn't a GoogleKeyProvider");
match validate_jwt(&jwt_body.jwt, provider_object).await {
{...}
}
}
validate_jwt then tries to call a method on the provider struct like this:
async fn validate_jwt(jwt: &String, provider: &GoogleKeyProvider) -> Result<bool, Box<dyn std::error::Error>> {
let key_to_use = provider.get_key_async(<thing>).await.unwrap();
}
Which presents me with this error:
error[E0596]: cannot borrow `*provider` as mutable, as it is behind a `&` reference
--> src\routes\validate.rs:48:22
|
48 | let key_to_use = provider.get_key_async(<thing>).await.unwrap();
| ^^^^^^^^ `provider` is a `&` reference, so the data it refers to cannot be borrowed as mutable
As far as I can understand, this is happening because the result of my downcasting is a reference (due to downcast_ref), but I think I'd be wanting the plain GoogleKeyProvider type instead - I'm not sure on that though. I believe the provider needs to be mutable as the values inside it (see below) can change during the lifetime of the provider (it's intended to provide a temporary cache for some keys, and automatically update them if they're out of date)
#[derive(Clone)]
pub struct GoogleKeyProvider {
cached: Option<JwkSet>,
expiration_time: Instant,
}
I'm not sure how to get this working with downcasting, though. Is anyone able to help me see where I've gone wrong?
You have to choice if get_key_async update somme thing at the struct.
The simple code below show you the error
trait Atrait {
fn afn(&mut self) -> i32;
}
struct Astruct {}
impl Atrait for Astruct {
fn afn(&mut self) -> i32 {
2
}
}
fn main()
{
// test should be mutable
let test = Astruct{};
let value = test.afn();
println!("Value {}", value);
}
This work because afn(self) is not declared mutable afn(&mut self)
trait Atrait {
fn afn(&self) -> i32;
}
struct Astruct {}
impl Atrait for Astruct {
fn afn(&self) -> i32 {
2
}
}
fn main()
{
let test = Astruct{};
let value = test.afn();
println!("Value {}", value);
}

How can I return an Arc<Vec<u8>> as a Hyper response?

I am writing a server that allocates some compressed data on startup. Now when I serve a hyper response I do not want to copy these bytes, but I cannot figure out a way to do this with hyper.
I have tried implementing HttpBody for my own type, but the lifetime restriction on the trait blocks me from doing this.
Am I missing something? Here is a minimal example of what I am trying to do:
use hyper::{service::Service, Body, Request, Response, Server};
use std::net::SocketAddr;
use std::sync::Arc;
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
fn main() {
let addr = SocketAddr::new("0.0.0.0".parse().unwrap(), 8080);
println!("Server startup...");
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let app = MakeSvc::new().await;
let ret = app.clone();
let server = Server::bind(&addr).serve(app);
println!("Running on {}", &addr);
server.await.unwrap();
})
}
#[derive(Debug, Clone)]
pub struct WrapperApp {
pub cache: Arc<Vec<u8>>,
}
impl WrapperApp {
//Let's say I allocate some bytes here.
async fn new() -> Self {
Self {
cache: Arc::new(Vec::new()),
}
}
}
impl Service<Request<Body>> for WrapperApp {
type Response = Response<Body>;
type Error = hyper::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
let a = Arc::clone(&self.cache);
return Box::pin(async { Ok(Response::builder().body(Body::from(a)).unwrap()) });
}
}
#[derive(Debug, Clone)]
pub struct MakeSvc {
app: WrapperApp,
}
impl MakeSvc {
pub async fn new() -> Self {
Self {
app: WrapperApp::new().await,
}
}
}
impl<T> Service<T> for MakeSvc {
type Response = WrapperApp;
type Error = hyper::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: T) -> Self::Future {
let app = self.app.clone();
let fut = async move { Ok(app) };
Box::pin(fut)
}
}
error[E0277]: the trait bound `Body: From<Arc<Vec<u8>>>` is not satisfied
--> src/main.rs:50:61
|
50 | return Box::pin(async { Ok(Response::builder().body(Body::from(a)).unwrap()) });
| ^^^^^^^^^^ the trait `From<Arc<Vec<u8>>>` is not implemented for `Body`
|
= help: the following implementations were found:
<Body as From<&'static [u8]>>
<Body as From<&'static str>>
<Body as From<Box<(dyn futures_core::stream::Stream<Item = std::result::Result<hyper::body::Bytes, Box<(dyn std::error::Error + Send + Sync + 'static)>>> + Send + 'static)>>>
<Body as From<Cow<'static, [u8]>>>
and 4 others
= note: required by `from`
The Cargo.toml that goes with this. The example breaks at the place where I am trying to use the ref:
[package]
name = "hyper-ptr"
version = "0.1.0"
authors = ["Pierre Laas <lanklaas123#gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
hyper={version="0.14", features=["server","http1","tcp"]}
tokio={version="1.0.0", features=["macros","io-util","rt-multi-thread"]}
serde={version="*", features=["derive","rc"]}
serde_json="*"
flate2="*"
openssl="*"
rand="*"
From the documentation, there is no From implementation that doesn't require either 'static references or ownership of the body.
Instead, you can clone the cache. Body::From's implementation requires ownership of the Vec<u8> or static data (if that is useful for your case).
Notice that you need to dereference it first:
use hyper::Body; // 0.14.2
use std::sync::Arc;
const FOO: [u8; 1] = [8u8];
fn main() {
let cache = Arc::new(vec![0u8]);
let body = Body::from((*cache).clone());
let other_body = Body::from(&FOO[..]);
println!("{:?}", cache);
}
Playground

Future and Stream nesting with type problems

I'd like to use a future which returns a Vec<String>, iterate over this in a future-stream and give the values to another future and the result of this future should be handled. The complete thing should be a future, too.
What's the way to go? I've tried different approaches and with all I've got type problems, which I don't understand.
Why there are these nested future result type signatures? Shouldn't this become the final result? Why doesn't the compiler know the types?
error[E0631]: type mismatch in closure arguments
--> src/lib.rs:45:18
|
45 | .then(|x: Result<(), ()>| ok(()))
| ^^^^ -------------------------- found signature of `fn(std::result::Result<(), ()>) -> _`
| |
| expected signature of `fn(std::result::Result<std::vec::Vec<tokio::prelude::future::Then<tokio::prelude::future::Then<impl tokio::prelude::Future, tokio::prelude::future::FutureResult<(), ()>, [closure#src/lib.rs:35:31: 41:26]>, tokio::prelude::future::FutureResult<(), _>, [closure#src/lib.rs:42:31: 42:57]>>, _>) -> _`
I've setup a Playground for this
extern crate tokio;
use tokio::prelude::future::ok;
use tokio::prelude::*;
#[allow(dead_code)]
pub fn test_future<F>(f: F) -> Result<F::Item, F::Error>
where
F: IntoFuture,
F::Future: Send + 'static,
F::Item: Send + 'static,
F::Error: Send + 'static,
{
let mut runtime = tokio::runtime::Runtime::new().expect("Unable to create a runtime");
runtime.block_on(f.into_future())
}
#[allow(dead_code)]
fn fut(el: &String) -> impl Future<Item = String, Error = std::io::Error> {
ok((el.to_string() + "-ok").to_string())
}
#[test]
fn reporting_future_result_test() {
let v = vec![
vec!["a".to_string(), "b".to_string()],
vec!["a".to_string(), "b".to_string()],
];
let f = stream::iter_ok(v.iter().cloned())
.map(|el: Vec<String>| {
stream::iter_ok(el.iter().cloned())
.map(|ell: String| {
fut(&ell)
.then(|x: Result<String, std::io::Error>| {
match x {
Ok(s) => println!("{}", s),
Err(e) => println!("{:?}", e),
};
ok(())
})
.then(|x: Result<(), ()>| ok(()))
})
.collect()
.then(|x: Result<(), ()>| ok(()))
})
.collect()
.then(|x: Result<Vec<_>, std::io::Error>| ok(()));
let r = test_future(f);
match r {
Ok(x) => println!("{:?}", x),
Err(_) => println!("error"),
}
}
extern crate tokio; // 0.1.11
use tokio::prelude::*;
// a future which returns a Vec<String>
fn makes_strings() -> impl Future<Item = Vec<String>, Error = ()> {
future::ok(vec![])
}
fn make_new_string(el: String) -> impl Future<Item = String, Error = ()> {
future::ok(el + "-ok")
}
fn iterate_over() -> impl Future<Item = Vec<String>, Error = ()> {
makes_strings().and_then(|v| {
// iterate over this
let strings = v.into_iter();
// give the values to another future
let futures = strings.map(make_new_string);
// The complete thing should be a future
future::join_all(futures)
})
}
future::ok
Future::and_then
future::join_all

Resources