Example of channel with non-trivial data?


#1

I just started with Rust this past week and ran into a compiler error I don’t know how to resolve. My goal is simple: call a function that sets up a channel which sends structs. The only examples I can find involving channels are sending nothing more complex than an integer. Does anyone know of any examples using channels to send and receive structured data?

In case anyone wants to see my ugly code and the compiler error:

//
// Stripped down example demonstrating the error.
//

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

type TokenType = i32;

struct Token<'a> {
    typ: TokenType,
    val: &'a str,
    row: i32,
    col: i32
}

fn make_chan<'a>() -> Receiver<Token<'a>> {
    let (tx, rx) = mpsc::sync_channel(1);
    rx
}

fn main() {
    let rx = make_chan();
    println!("received something");
}

$ rustc main.rs
main.rs:18:20: 18:38 error: declared lifetime bound not satisfied
main.rs:18     let (tx, rx) = mpsc::sync_channel(1);
                              ^~~~~~~~~~~~~~~~~~
main.rs:17:43: 20:2 note: lifetime parameter instantiated with the lifetime 'a as defined on the block at 17:42
main.rs:17 fn make_chan<'a>() -> Receiver<Token<'a>> {
main.rs:18     let (tx, rx) = mpsc::sync_channel(1);
main.rs:19     rx
main.rs:20 }
note: but lifetime parameter must outlive the static lifetime
error: aborting due to previous error

Thanks for any help you can offer. I’m really looking forward to getting to know Rust. I’ve been admiring the language for quite some time.

n


#2

The problem you’re running into here is because a channel can only be created for data that satisfies the Send trait, since the definition of sync_channel is:

pub fn sync_channel<T: Send>(bound: usize) -> (SyncSender<T>, Receiver<T>)

The Send trait represents data which can be moved across thread boundaries. This implies the 'static lifetime.

It wouldn’t be safe to send a borrowed string (w/ a non-static lifetime) to another task because rustc has no way of proving that the reference will live long enough.

Say you have thread A which owns the string, and you send a borrowed reference to thread B over a channel.

What happens if thread A sends the reference to the buffered channel, but then it panicks before thread B spawns and calls recv()?

Thread B would now have a pointer to freed memory, which is exactly what Rust aims to prevent.


If you want your struct to be Sendable you could use String, here’s an example of that on the playpen based on your code.

If you want to share data between threads, instead of sending it, you’ll need to wrap the data in some of the sync primitives like Arc, Mutex, RwLock, etc.

  • Arc is a reference counted pointer to shared immutable data that is thread-safe.
  • Mutex / RwLock can be used to protect mutable data with locks.

Hope this has been of some help :slight_smile:


#3

You have been a fantastic help, thank you. In my searching I had come across a mention of the Send trait but I didn’t know what to make of the docs. Thanks also for the tip on when to use String. That makes sense, and your example is perfect.

Thank you very much.


#4

I suggest copying the question (with @nlfiedler’s permission) and your answer and posting them on stackoverflow. This is likely to be a frequenty asked question, and more people are likely to find it by googling if it is on SO.


#5

Sounds good to me. Thanks.