Search code examples
rustfuturerust-tokio

How do I use a Tokio oneshot sender and receiver on different tasks with inner loops?


Not sure how to handler the borrow checker here.

use tokio::sync::oneshot; // 1.0.2

fn main() {
    let (sender, receiver) = oneshot::channel::<u8>();
    tokio::spawn(async move {
        loop {
            sender.send(3).unwrap();
        }
    });
}

Creates this error:

error[E0382]: use of moved value: `sender`
 --> src/main.rs:7:13
  |
7 |             sender.send(3).unwrap();
  |             ^^^^^^ value moved here, in previous iteration of loop
  |
  = note: move occurs because `sender` has type `tokio::sync::oneshot::Sender<u8>`, which does not implement the `Copy` trait

Solution

  • You don't. That's the entire point of a oneshot channel: it can only be used a maximum of one time:

    A channel for sending a single message between asynchronous tasks.

    tokio::sync::oneshot

    It sounds like you want a different kind of channel, such as tokio::sync::mpsc.


    what if I only want to send one message at the start of the loop?

    Then perform the code before the loop starts:

    use tokio::sync::oneshot; // 1.0.2
    
    fn main() {
        let (sender, receiver) = oneshot::channel::<u8>();
        tokio::spawn(async move {
            sender.send(3).unwrap();
            
            loop {
                // Do things
            }
        });
    }
    

    If you had to have it inside the loop, you need to dynamically mark the value as no longer there and handle that case. Here, I use an Option and if let:

    use tokio::sync::oneshot; // 1.0.2
    
    fn main() {
        let (sender, receiver) = oneshot::channel::<u8>();
        tokio::spawn(async move {
            let mut sender = Some(sender);
            
            loop {
                if let Some(sender) = sender.take() {
                    sender.send(3).unwrap();
                }
                // Do things
            }
        });
    }