Reputation: 8913
The following code works... but ... I am looking for a generic way to setup a channel receiver in a way that I can transmit any struct type value
The following async function setup
returns a tx
object and at compile time it understands it is of type CIRCLE
, and the inner receiver (rx
) will expect CIRCLEs... although I´d want to write a generic function that can receive any sort of object... say RECTANGLES, CIRCLES, etc... Is this possible with Generics ?
use serde::__private::de::IdentifierDeserializer;
use serde::{Deserialize, Serialize};
use serde_json::json;
use tokio::sync::mpsc;
#[derive(Debug)]
pub struct Shape {
pub id: Option<String>,
}
#[derive( Debug)]
pub struct Circle {
pub id: Option<String>,
pub radius: f32,
}
#[derive( Debug)]
pub struct Rectangle {
pub id: Option<String>,
pub width: f32,
pub height: f32,
}
// QUESTION: telling compiler that TX will be of Circle type but how to make it work for also Rectangle ?
pub async fn setup() -> mpsc::Sender<Circle> {
let (tx, mut rx) = mpsc::channel(1);
let handle = tokio::spawn(async move {
while let Some(my_shape) = rx.recv().await {
println!("... received a shape: {:?}", my_shape);
};
});
tx
}
#[tokio::main]
async fn main() {
let tx = setup().await;
let my_shape = Circle{id: None, radius: 2.0};
// let my_shape = Rectangle{id: None, width: 3.0, height: 4.0}; <== WILL NOT WORK
let handle = tokio::spawn(async move {
let _ = tx.send(my_shape).await;
});
loop {};
}
Upvotes: 0
Views: 277
Reputation: 22838
If you want to have several types that all have some common functionality, you want to bundle the common functionality into trait
s.
Then, instead of telling the channel to transport specific objects, you can tell the channel to transport all objects that implement said trait.
The only thing you have to do additionally is to Box
the types. By telling the channel to transport objects of a specific trait, Rust looses the information about how big those objects are, because it could be anything. Therefore, by packaging them in a Box
(meaning: putting them behind one pointer indirection on the heap) it doesn't matter for the compiler how big they are, because it only handles the Box item that references it, and Boxes are always identical in size. (They don't contain the data, they only point to it somewhere in the heap).
If applied to your code, it could look like this:
use std::fmt::Debug;
use tokio::sync::mpsc;
use tokio::time::{sleep, Duration};
pub trait Shape: Debug + Send + Sync {
fn get_id(&self) -> Option<String>;
}
#[derive(Debug)]
pub struct Circle {
pub id: Option<String>,
pub radius: f32,
}
impl Shape for Circle {
fn get_id(&self) -> Option<String> {
self.id.clone()
}
}
#[derive(Debug)]
pub struct Rectangle {
pub id: Option<String>,
pub width: f32,
pub height: f32,
}
impl Shape for Rectangle {
fn get_id(&self) -> Option<String> {
self.id.clone()
}
}
pub async fn setup() -> mpsc::Sender<Box<dyn Shape>> {
let (tx, mut rx) = mpsc::channel(1);
tokio::spawn(async move {
while let Some(my_shape) = rx.recv().await {
println!("... received a shape: {:?}", my_shape);
}
});
tx
}
#[tokio::main]
async fn main() {
let tx = setup().await;
// let my_shape = Rectangle{id: None, width: 3.0, height: 4.0}; <== WILL NOT WORK
tx.send(Box::from(Circle {
id: None,
radius: 2.0,
}))
.await
.unwrap();
tx.send(Box::from(Rectangle {
id: Some("my_id".to_string()),
width: 10.0,
height: 15.3,
}))
.await
.unwrap();
sleep(Duration::from_millis(100)).await
}
... received a shape: Circle { id: None, radius: 2.0 }
... received a shape: Rectangle { id: Some("my_id"), width: 10.0, height: 15.3 }
Further feedback for your original code:
await
in them in async programming. This will block the event loop, which stops everything. Always at least put a sleep(..).await
in there.serde
imports because they had nothing to do with the question on hand. Also, probably don't use serde::__private
, because it's not meant to be used by the end-user.handle
variables if you don't use them.let _ =
. Handle them, maybe with unrwap()
, but at least with an error message. Completely hiding then can cause a lot of frustration when searching for problems.Upvotes: 2