Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rust equivalent of C++ std::promise and std::future

Tags:

rust

In C++ we have std::promise and std::future, which will sometimes help in getting the result or status of a task executed by a thread in another thread, for example main thread.

How can the same be achieved in Rust? Basically I'm looking for a (non-async/tokio) Rust equivalent of the below C++ code.

#include <chrono>
#include <future>
#include <iostream>
#include <thread>

void foo(std::promise<int>&& p) {
    auto p1 = std::move(p);
    using namespace std::chrono_literals;
    std::this_thread::sleep_for(5000ms);
    p1.set_value(42);
}

int main() {
    std::promise<int> p;
    std::future<int> f = p.get_future();

    std::jthread jt1(foo, std::move(p));

    std::cout << "waiting" << std::endl;
    std::cout << f.get() << std::endl;
    jt1.join();
}
like image 201
Harry Avatar asked Dec 02 '25 22:12

Harry


1 Answers

Sync rust does not have futures (and the future trait doesn't really have the properties you're seeking).

There are a few options though neither is an exact match for a future

return from the thread

Rust threads can return values, so having a side-channel is not necessary if your thread is a one-shot worker, JoinHandle::join does the job:

fn main() {
    let t = std::thread::spawn(foo);

    println!("{:?}", t.join());
}

fn foo() -> i32 {
    std::thread::sleep(std::time::Duration::from_millis(500));
    42
}

a channel

Oneshot is ideal but the stdlib doesn't have one, mpsc is a bit worse because it can't be consumed (and could have multiple producers) but probably good enough in most situations

use std::sync::mpsc::{sync_channel, SyncSender};

fn main() {
    let (s, r) = sync_channel(0);
    let t = std::thread::spawn(move || foo(s));

    println!("{:?}", r.recv());
    t.join().unwrap();
}

fn foo(c: SyncSender<i32>) {
    std::thread::sleep(std::time::Duration::from_millis(500));
    c.send(42).unwrap();
}

OnceLock

This is a pretty recent addition to the stdlib, and has the drawback of being symmetrical (rather than having a reader and a writer side), but you can easily wrap it into a sender and a receiver if you want to force more promise-style properties

use std::sync::{Arc, OnceLock};

fn main() {
    let l = Arc::new(OnceLock::new());
    let t = std::thread::spawn({
        let l = l.clone();
        move || foo(l)
    });

    println!("{:?}", l.wait());
    t.join().unwrap();
}

fn foo(l: Arc<OnceLock<i32>>) {
    std::thread::sleep(std::time::Duration::from_millis(500));
    l.set(42).unwrap();
}
like image 68
Masklinn Avatar answered Dec 05 '25 17:12

Masklinn