0

Scala has a way to convert a iterable of futures to a single future of iterables via Futures.sequence

I was searching for same in Rust and found the futures_util crate. I used this crate in a program edited from the Actix example, but it failed to compile.

Cargo.toml

[package]
name = "actix"
version = "0.7.10"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actor framework for Rust"
readme = "README.md"
keywords = ["actor", "futures", "actix", "async", "tokio"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix.git"
documentation = "https://docs.rs/actix/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"]

[badges]
travis-ci = { repository = "actix/actix", branch = "master" }
appveyor = { repository = "fafhrd91/actix-n9e64" }
codecov = { repository = "actix/actix", branch = "master", service = "github" }

[lib]
name = "actix"
path = "src/lib.rs"

[workspace]
members = ["examples/chat"]

[features]
default = ["signal", "resolver"]

# dns resolver
resolver = ["trust-dns-resolver", "trust-dns-proto"]

# signal handling
signal = ["tokio-signal"]

[dependencies]
actix_derive = "0.3"

# io
bytes = "0.4"
futures = "0.1"
futures-util = "0.2.1"
tokio = "0.1.7"
tokio-io = "0.1"
tokio-codec = "0.1"
tokio-executor = "0.1"
tokio-reactor = "0.1"
tokio-tcp = "0.1"
tokio-timer = "0.2"

# other
log = "0.4"
fnv = "1.0.5"
failure = "0.1.1"
bitflags = "1.0"
smallvec = "0.6"
crossbeam-channel = "0.3"
parking_lot = "0.7"
uuid = { version = "0.7", features = ["v4"] }

# signal handling
tokio-signal = { version = "0.2", optional = true }

# dns resolver
trust-dns-proto = { version = "^0.5.0", optional = true }
trust-dns-resolver = { version = "^0.10.0", optional = true }

[target.'cfg(unix)'.dependencies]
libc = "0.2"

[profile.release]
lto = true
opt-level = 3
codegen-units = 1

Code :

extern crate actix;
extern crate futures;
extern crate tokio;
extern crate futures_util;

use actix::prelude::*;
use futures::Future;
use futures_util::future::*;
use std::time::{SystemTime, UNIX_EPOCH};

/// Define `Ping` message
struct Ping(usize);

impl Message for Ping {
    type Result = usize;
}

/// Actor
struct MyActor {
    count: usize,
}

/// Declare actor and its context
impl Actor for MyActor {
    type Context = Context<Self>;
}

/// Handler for `Ping` message
impl Handler<Ping> for MyActor {
    type Result = usize;

    fn handle(&mut self, msg: Ping, _: &mut Context<Self>) -> Self::Result {
        self.count += msg.0;
        self.count
    }
}

fn main() {
    // start system, this is required step
    System::run(|| {
        // start new actor
        let addr = MyActor { count: 10 }.start();

        let start = SystemTime::now();

        // send message and get future for result
        let res =  join_all((1..10).into_iter().map(|x| addr.send(Ping(x))));

        // handle() returns tokio handle
        tokio::spawn(
       res.map(|res| {
           let difference = start.duration_since(start)
                          .expect("SystemTime::duration_since failed");
           println!("Time taken: {:?}", difference);

           // stop system and exit
           System::current().stop();
       }).map_err(|_| ()),
        );
    });
}

Even though the error is meaningful, I find it difficult to resolve as Request in Actix implements Future. Did I miss any imports?

error[E0277]: the trait bound `actix::prelude::Request<MyActor, Ping>: futures_core::future::Future` is not satisfied
  --> examples/ping.rs:47:20
   |
47 |         let res =  join_all((1..10).into_iter().map(|x| addr.send(Ping(x))));
   |                    ^^^^^^^^ the trait `futures_core::future::Future` is not implemented for `actix::prelude::Request<MyActor, Ping>`
   |
   = note: required because of the requirements on the impl of `futures_core::future::IntoFuture` for `actix::prelude::Request<MyActor, Ping>`
   = note: required by `futures_util::future::join_all`

error[E0277]: the trait bound `actix::prelude::Request<MyActor, Ping>: futures_core::future::Future` is not satisfied
  --> examples/ping.rs:47:20
   |
47 |         let res =  join_all((1..10).into_iter().map(|x| addr.send(Ping(x))));
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `futures_core::future::Future` is not implemented for `actix::prelude::Request<MyActor, Ping>`
   |
   = note: required by `futures_util::future::JoinAll`

error[E0599]: no method named `map` found for type `futures_util::future::JoinAll<actix::prelude::Request<MyActor, Ping>>` in the current scope
  --> examples/ping.rs:55:12
   |
55 |        res.map(|res| {
   |            ^^^
   |
   = note: the method `map` exists but the following trait bounds were not satisfied:
           `futures_util::future::JoinAll<actix::prelude::Request<MyActor, Ping>> : futures_util::FutureExt`
           `&futures_util::future::JoinAll<actix::prelude::Request<MyActor, Ping>> : futures_util::FutureExt`
           `&mut futures_util::future::JoinAll<actix::prelude::Request<MyActor, Ping>> : futures_util::FutureExt`
           `&mut futures_util::future::JoinAll<actix::prelude::Request<MyActor, Ping>> : futures::Future`
           `&mut futures_util::future::JoinAll<actix::prelude::Request<MyActor, Ping>> : std::iter::Iterator`
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
manu
  • 223
  • 1
  • 2
  • 12
  • well, first futures is a concept free langage, also there is many way to implement it, rust is trying to improve this concept by adding it "directly" in the language https://github.com/rust-lang/rust/issues/50547. futures crates is still under development and will change a lot. So yes the doc is very poor currently. Also your question is unclear, actix currently is build on top of tokio that it's build on top of many thing. "iterable of futures to a single Future of iterable" don't see the difference. – Stargateur Dec 25 '18 at 03:21
  • Also I am eagerly waiting for stable futures :). Yes tokio uses many other things but basic future is still same right. Or does each implement their own futures? If yes then they should give a way to convert their Future to standard rust Future. – manu Dec 25 '18 at 04:48
  • @Stargateur Well i mean like Vec> to Future>. futures-util crate has join_all function defined for the same. But don't know how to use it. I am a totally Rust beginner here. – manu Dec 25 '18 at 05:11
  • you mean `Vec` to `Future>` ? you sure you don't want use [`iter_ok()`](https://docs.rs/futures/0.1.25/futures/stream/fn.iter_ok.html) ? – Stargateur Dec 25 '18 at 05:11
  • 1
    haha begin rust with actix is clearly not a good idea ^^ it's like learn C by doing a HTTP server. – Stargateur Dec 25 '18 at 05:12
  • Oh I understand your problem, wait I will try to on my machine probably a versoin problem indeed – Stargateur Dec 25 '18 at 05:18
  • 1
    your cargo.toml don't have actix crate also your question is far than from having a [mcve] keep it simple. why there is a lib, a workspace and all stuff that I ignore and are not linking to the question ? Seem like you just copy actix toml file – Stargateur Dec 25 '18 at 05:26
  • @Stargateur yup i did copy of actix cargo.toml. because I edited example file provided in actix. Due to which I need not put dependency on actix. – manu Dec 25 '18 at 05:35
  • iter_ok? let me try that. Also let me provide a simple example by creating a new project instead of editing what actix has given in examples. – manu Dec 25 '18 at 05:36
  • @Stargateur, haha I agree, Need to start from simple programs. But the problem is, I work in environment of distributed computing in scala, akka, spark etc.. So I really dont get enough time to start from beginning. But reading thru docs of rust I can fairly understand stuff. Many things translates to the way scala is. So a direct jump. – manu Dec 25 '18 at 05:42

1 Answers1

1

In your project, you use the join_all function of futures included in futures-util. It seems like this crate is in conflict with the actix version of futures.

In actix 0.7.10

futures = "0.1"

in futures-util 0.2.1:

futures = "~0.1.15"

I advise you to directly use join_all from futures:

[package]
name = "Battlefield Vietnam"
version = "0.0.1"

[dependencies]
actix = "0.7"
futures = "0.1"
tokio = "0.1.7"
extern crate actix;
extern crate futures;
extern crate tokio;

use actix::prelude::*;
use futures::future::*;
use futures::Future;
use std::time::SystemTime;

/// Define `Ping` message
struct Ping(usize);

impl Message for Ping {
    type Result = usize;
}

/// Actor
struct MyActor {
    count: usize,
}

/// Declare actor and its context
impl Actor for MyActor {
    type Context = Context<Self>;
}

/// Handler for `Ping` message
impl Handler<Ping> for MyActor {
    type Result = usize;

    fn handle(&mut self, msg: Ping, _: &mut Context<Self>) -> Self::Result {
        self.count += msg.0;
        self.count
    }
}

fn main() {
    // start system, this is required step
    System::run(|| {
        // start new actor
        let addr = MyActor { count: 10 }.start();

        let start = SystemTime::now();

        // send message and get future for result
        let res = join_all((1..10).into_iter().map(move |x| addr.send(Ping(x))));

        // handle() returns tokio handle
        tokio::spawn(
            res.map(move |res| {
                let difference = start
                    .duration_since(start)
                    .expect("SystemTime::duration_since failed");
                println!("Time taken: {:?}", difference);

                // stop system and exit
                System::current().stop();
            })
            .map_err(|_| ()),
        );
    });
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Stargateur
  • 24,473
  • 8
  • 65
  • 91