I would like to have something like Java 8 CompletableFuture, something that could be passed around between threads, so that one thread could wait for another to complete a task with a result.
I found a crate providing such a functionality (completable_future) that underneath uses a Mutex. But instead I decided to try using a futures::sync::oneshot channel, which is probably much heavier, but the implementation would be much easier to understand, because the goal of oneshot is basically the same as of the CompletableFuture. I also wanted to use it as an opportunity to learn Rust (I am a beginner).
But then I entered the ownership hell, which led me to questioning if Sender and Receiver can be passed around together at all. And also questioning the ability to build the CompletableFuture abstraction.
So back to requirements. I would like to have is interface which would allow me doing thing like this:
use std::thread;
mod completable_future;
use completable_future::CompletableFuture;
use futures::future::Future;
use std::sync::Arc;
fn main() {
let c1 = Arc::new(CompletableFuture::<u64>::new());
let c2 = Arc::new(CompletableFuture::<bool>::new());
let cloned_c1 = c1.clone();
let cloned_c2 = c2.clone();
let child = thread::spawn(move || {
let c1 = cloned_c1;
let c2 = cloned_c2;
let result = 5; // in real life -> let result = some_lengthy_calculations_part1();
c1.complete(result);
let result = false; // in real life -> let result = some_long_calculations_part2();
c2.complete(result);
});
let result_future1 = c1.into_future()
.and_then(|x| {
println!("Doing something with partial result1: {}", x);
Ok(())
});
let result_future2 = c2.into_future()
.and_then(|x| {
println!("Doing something with partial result2: {}", x);
Ok(())
});
result_future1.wait();
result_future2.wait();
child.join();
}
The implementation I came up with is
use futures::sync::oneshot;
use futures::future::Future;
pub struct CompletableFuture<T> {
receiver : oneshot::Receiver<T>,
sender : oneshot::Sender<T>,
}
impl<T> CompletableFuture<T> {
pub fn new() -> CompletableFuture<T>
{
let (sender, receiver) = oneshot::channel::<T>();
CompletableFuture {
sender,
receiver
}
}
pub fn into_future(&self) -> impl Future<Item =T, Error = oneshot::Canceled> {
self.receiver
}
pub fn complete(&self, value: T) -> Result<(), T> {
self.sender.send(value)
}
}
Currently this gives (playground):
Compiling playground v0.0.1 (/playground)
error[E0507]: cannot move out of borrowed content
--> src/main.rs:22:9
|
22 | self.receiver
| ^^^^^^^^^^^^^ cannot move out of borrowed content
error[E0507]: cannot move out of borrowed content
--> src/main.rs:26:9
|
26 | self.sender.send(value)
| ^^^^^^^^^^^ cannot move out of borrowed content
Would you have any suggestion on how to build the abstraction?