-1

I want to run an event loop in one thread and handle data from a UDP socket until another thread signals to stop work.

This is a difficult task for me, so I want to start from a simpler task: one thread starting the event loop and waiting for another thread to signal the end:

use futures::{future, future::Future, stream::Stream, sync::mpsc};
use std::{io, io::BufRead, thread};

fn main() {
    let (mut tx, rx) = mpsc::channel::<bool>(1);

    let thr = thread::spawn(|| {
        let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap();
        runtime.spawn(
            future::lazy(|| {
                println!("event loop started");
                Ok(())
            })
            .and_then(rx.take_while(|x| *x == true).into_future()),
        );

        runtime.run()
    });

    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        let line = line.unwrap();
        println!("{}", line);
        if line == "exit" {
            tx.try_send(false).unwrap();
            break;
        }
    }
    thr.join().unwrap().unwrap();
}

This code doesn't compile:

error[E0277]: the trait bound `bool: futures::future::Future` is not satisfied
  --> src/main.rs:14:26
   |
14 |             .and_then(rx.take_while(|x| *x == true).into_future()),
   |                          ^^^^^^^^^^ the trait `futures::future::Future` is not implemented for `bool`
   |
   = note: required because of the requirements on the impl of `futures::future::IntoFuture` for `bool`

error[E0599]: no method named `into_future` found for type `futures::stream::take_while::TakeWhile<futures::sync::mpsc::Receiver<bool>, [closure@src/main.rs:14:37: 14:51], bool>` in the current scope
  --> src/main.rs:14:53
   |
14 |             .and_then(rx.take_while(|x| *x == true).into_future()),
   |                                                     ^^^^^^^^^^^
   |
   = note: the method `into_future` exists but the following trait bounds were not satisfied:
           `futures::stream::take_while::TakeWhile<futures::sync::mpsc::Receiver<bool>, [closure@src/main.rs:14:37: 14:51], bool> : futures::stream::Stream`
           `&mut futures::stream::take_while::TakeWhile<futures::sync::mpsc::Receiver<bool>, [closure@src/main.rs:14:37: 14:51], bool> : futures::stream::Stream`

How do I fix the compilation error?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
user1244932
  • 7,352
  • 5
  • 46
  • 103

1 Answers1

0

Read and understand the documentation and function signature of methods you attempt to use:

fn take_while<P, R>(self, pred: P) -> TakeWhile<Self, P, R>
where
    P: FnMut(&Self::Item) -> R,
    R: IntoFuture<Item = bool, Error = Self::Error>,
    Self: Sized, 

take_while takes a closure that returns some type that must be convertible into a future; a bool is not convertible into a future. The simplest way to do this is via future::ok:

let thr = thread::spawn(|| {
    let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap();
    runtime.spawn({
        rx.take_while(|&x| future::ok(x))
            .for_each(|x| {
                println!("{}", x);
                future::ok(())
            })
    });

    runtime.run()
});

See also:

But my problem also in joining future::lazy and rx.take_while

That's an unrelated problem to what you asked about. Again, we look at the docs, this time for Future::and_then:

fn and_then<F, B>(self, f: F) -> AndThen<Self, B, F>
where
    F: FnOnce(Self::Item) -> B,
    B: IntoFuture<Error = Self::Error>,
    Self: Sized, 

Similarly to take_while, it takes a closure and the closure must return something that can be convertible into a future. Your code doesn't provide a closure.

Then look at Stream::into_future. This returns a type that implements Future and returns a tuple. The first item in the tuple is a single value from the stream, the second is the stream itself, to allow getting more values.

To get all the item and error types correct, I've make liberal use of map(drop) and map_err(drop) — you will want to do something better for your data and error handling.

runtime.spawn({
    future::lazy(|| {
        println!("event loop started");
        Ok(())
    })
    .and_then(|_| {
        rx.take_while(|&x| future::ok(x))
            .into_future()
            .map(drop)
            .map_err(drop)
    })
    .map(drop)
});

Really, you should just use a oneshot channel; it's much simpler:

use futures::{
    future::{self, Future},
    sync::oneshot,
};
use std::thread;

fn main() {
    let (tx, rx) = oneshot::channel();

    let thr = thread::spawn(|| {
        let mut runtime = tokio::runtime::current_thread::Runtime::new().unwrap();

        runtime.spawn({
            future::lazy(|| {
                println!("event loop started");
                Ok(())
            })
            .and_then(|_| rx.map_err(drop))
        });

        runtime.run()
    });

    let lines = ["hello", "goodbye", "exit"];
    for &line in &lines {
        if line == "exit" {
            tx.send(()).unwrap();
            break;
        }
    }

    thr.join().unwrap().unwrap();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    As an alternative to avoid for_each : `rx.take_while(|x| futures::future::ok(*x == true)).into_future().then(|_| Ok(())) ` works also . – Ömer Erden Jan 17 '19 at 20:05
  • But my problem also in joining `future::lazy` and `rx.take_while` – user1244932 Jan 17 '19 at 20:11
  • @user1244932 updated. – Shepmaster Jan 18 '19 at 00:36
  • @ÖmerErden please don't feel like you need to delete your answer if it's still correct; you recognized that the OP wanted the chaining and `into_future` before I did. It's always possible that the OP thinks that your answer better fits their code. – Shepmaster Jan 18 '19 at 04:25