Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending different types using same Rust channel (mpsc)

I've got a situation where I am setting up channel with multiple senders that need to be able to send different types of data to a receiving thread.

A receiving thread is created to handle these messages using the following match expression.

let receiver_thread = match config.style.as_str() {
    "Type1" => start_recv_type1(receiver, config.clone(), log.clone()),
    "Type2" => start_recv_type2(receiver, config.clone(), log.clone()),
    "Type3" => start_recv_type3(receiver, log.clone()),
    _ => panic!("Wrong type!"),
};

One possible type that can be send is a String while the other is a struct I've defined. However, the compiler is complaining about mismatched types on the receiving thread. Looking at how my receiver and sender is defined I can see why this is incorrect because it has the following type: std::sync::mpsc::Receiver<std::string::String>.

Looking at my match expression, I think it's wiser to create a more generic function to initialize my receiving thread but how can I send different types of data over the same channel? I've searched and found a possible solution by using an enum that has my struct and a string as fields but does that mean I will have to change all my function definitions to use use this enum syntax? I.e. change my functions to use ChannelTypes(StringMessage(String)) or ChannelTypes(StructMessage(factory::datatype::MyStruct))

enum ChannelTypes {
    StructMessage(factory::datatype::MyStruct),
    StringMessage(String),
}
like image 556
Vinnie Avatar asked Oct 29 '19 21:10

Vinnie


2 Answers

One particular channel can only send one type of data. As you noticed, your sender has the type Sender<String> and the receiver has the type Receiver<String>. So both are fixed on String. There is no way around this (for good reasons!)

The preferred solution is actually to use an enum, yes. In fact, you try to emulate what an enum would do for you: you have a tag that describes what kind of data to expect. In your attempt you use strings as tags. But that's a bad idea for a multitude of reasons. When you use an enum, integer tags (much better) are used and Rust handles them for you. Much more robust solution.

One suggestion: you might find it more useful to think of your enum type as message. As that's what it is: you send a message to another thread. And this message could be one of many different types. The receiving thread has to check which kind of message arrived and can then handle it.

like image 188
Lukas Kalbertodt Avatar answered Nov 11 '22 11:11

Lukas Kalbertodt


Here's an example with enum:

use std::sync::mpsc::{self, Receiver, Sender};

enum Fruit {
    Apple(u8),
    Orange(String)
}

fn main() {
    let (tx, rx): (Sender<Fruit>, Receiver<Fruit>) = mpsc::channel();

    tx.send(Fruit::Orange("sweet".to_string())).unwrap();
    tx.send(Fruit::Apple(2)).unwrap();

    for _ in 0..2 {
        match rx.recv().unwrap() {
            Fruit::Apple(count) => println!("received {} apples", count),
            Fruit::Orange(flavor) => println!("received {} oranges", flavor),
        }
    }
}

// output:
// received sweet oranges
// received 2 apples

like image 5
d9ngle Avatar answered Nov 11 '22 09:11

d9ngle