1

I have a bunch of async_std::UdpSocket and want to drive them all until the first one completes, then get the result from its corresponding u8 buffer.

I've tried

// build sockets and buffers
let sockets = Vec::new();
let buffers = HashMap::new();
for addr in ["127.0.0.1:4000", "10.0.0.1:8080", "192.168.0.1:9000"] {
    let socket = UdpSocket::bind(addr.parse().unwrap()).await?;
    let buf = [0u8; 1500];
    sockets.push(socket);
    buffers.insert(socket.peer_addr()?, buf);
}

// create an iterator of tasks reading from each socket
let socket_tasks = sockets.into_iter().map(|socket| {
    let socket_addr = socket.peer_addr().unwrap();
    let buf = buffers.get_mut(&socket_addr).unwrap();
    socket
        .recv_from(buf)
        .and_then(|_| async move { Ok(socket_addr) })
});

// wait for the first socket to return a value (DOESN'T WORK)
let buffer = try_join_all(socket_tasks)
    .and_then(|socket_addr| async { Ok(buffers.get(&socket_addr)) })
    .await

However this does not work because futures::future::try_join_all drives all futures to completion and returns a Vec<(SocketAddr)>. I want to get the result when a single future completes and get just a SocketAddr--something akin to select! but over a collection.

How can this be done?

Ben Little
  • 57
  • 5

2 Answers2

1

FuturesUnordered is a Stream that will produce the results of futures in the order they complete. You can socket_tasks.collect() into this type, and then use the next() method of StreamExt to create a future that will complete when the first future in the whole collection completes.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • Will this return when all futures are finished or only the first? "to create a future that will complete when the first future in the whole collection completes." is the confusing part to me. – Brandon Ros Nov 09 '22 at 18:02
  • @BrandonRos The term "first" I think is what's confusing here. The stream will produce results as the futures complete; it doesn't have to be the first future added to the collection, but once any future completes, the result will be immediately emitted by the stream. It will not wait for all futures to complete. – cdhowie Nov 11 '22 at 23:48
1

If you only need the first future you can also use futures::future::select_all(). If there are many futures FuturesUnordered may be more performant because it only polls future when they awake, but if there are few select_all() is likely to be more performant as it is doing less work.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77