I don't think it is possible to do this directly, but if your type T can be serialized, then here is a way. Have listeners
be a Vec<Arc<dyn Fn(String)>>
, and when inserting to this Vec, pass a lambda that converts from String to T and calls the actual listener function. Here is a working code of what I mean:
use std::sync::Arc;
struct EventEmitter {
listeners: Vec<Arc<dyn Fn(String)>>,
}
impl EventEmitter {
pub fn add_listener<T>(&mut self, listener: Arc<dyn Fn(T)>)
where
T: std::str::FromStr, // So that `s` can be converted to type `t`.
<T as std::str::FromStr>::Err: std::fmt::Debug, // So that `e` can be printed.
T: 'static, // See https://stackoverflow.com/a/29740792/8111265
{
self.listeners.push(Arc::new(move |s| {
match s.parse::<T>() {
Ok(t) => listener(t),
Err(e) => println!("Oops! we couldn't convert {:?} to type T due to {:?}", s, e),
};
}));
}
pub fn notify(&self, s: &str) {
for listener in self.listeners.iter() {
listener(s.to_string());
}
}
}
#[test]
fn test_event_emitter() {
let mut e = EventEmitter { listeners: vec![] };
// i32 implements `std::str::FromStr`.
e.add_listener(Arc::new(|x: i32| {
println!("Got {:?} in i32 listener", x);
}));
// std::net::IpAddr implements `std::str::FromStr`.
e.add_listener(Arc::new(|ip_addr: std::net::IpAddr| {
println!("Got {:?} in IpAddr listener", ip_addr);
}));
// This line prints:
// Got 42 in i32 listener
// Oops! we couldn't convert "42" to type T due to AddrParseError(())
e.notify("42");
// This line prints:
// Oops! we couldn't convert "127.0.0.1" to type T due to ParseIntError { kind: InvalidDigit }
// Got V4(127.0.0.1) in IpAddr listener
e.notify("127.0.0.1");
}
The idea can be refined a bit more: perhaps some Arc
s will not be needed, and maybe there is a better 'base' type than String (possibly this can be made to work with any type that works with serde), you can use &str
instead of String as it is.
Since you asked about using serde, here's an example:
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct PointInts {
x: i32,
y: i32,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct PointFloats {
x: f32,
y: f32,
}
struct EventEmitter {
listeners: Vec<Arc<dyn Fn(&[u8])>>,
}
impl EventEmitter {
pub fn add_listener<T>(&mut self, listener: Arc<dyn Fn(T)>)
where
T: serde::de::DeserializeOwned,
T: 'static, // See https://stackoverflow.com/a/29740792/8111265
{
self.listeners.push(Arc::new(move |bytes| {
match bincode::deserialize(bytes) {
Ok(t) => listener(t),
Err(e) => println!(
"Oops! we couldn't convert the bytes {:?} to type T due to {:?}",
bytes, e
),
};
}));
}
pub fn notify<T>(&self, obj: T)
where
T: serde::Serialize,
{
let bytes = bincode::serialize(&obj).unwrap();
for listener in self.listeners.iter() {
listener(&bytes);
}
}
}
#[test]
fn test_event_emitter() {
let mut e = EventEmitter { listeners: vec![] };
// PoinitInts implements Serialize and Deserialize.
e.add_listener(Arc::new(|p: PointInts| {
println!("Got {:?} in PointInts listener", p);
}));
// PointFloats implements Serialize and Deserialize.
e.add_listener(Arc::new(|p: PointFloats| {
println!("Got {:?} in PointFloats listener", p);
}));
// This line prints:
// Got PointInts { x: 42, y: 999 } in PointInts listener
// Got PointFloats { x: 0.000000000000000000000000000000000000000000059, y: 0.0000000000000000000000000000000000000000014 } in PointFloats listener
e.notify(PointInts { x: 42, y: 999 });
// This line prints:
// Got PointInts { x: 1109917696, y: 1120327434 } in PointInts listener
// Got PointFloats { x: 42.0, y: 99.42 } in PointFloats listener
e.notify(PointFloats { x: 42.0, y: 99.420 });
}
Note that bincode::deserialize
will return Ok(_)
if the bytes can be converted to the given requested struct, and hence why you see weird values above (there may be a way to add a type tag so that 'wrong' structs don't get deserialized).