Hi there - I have a bunch of Events. Each event has the same envelope (timestamp, unique ID etc.) but the detail of the event differs.
I've modelled this as an EventDetail
enum and an Event
struct whose detail
field is of type EventDetail
.
This works well - type safe, exhaustive matches etc. but it is also really painful to have factory methods for the events because the Event
struct isn't typed based on the EventDetail
so every consumer of an event, even if the consumer KNOWS the type of detail needs to match on the detail
.
#[derive(Debug)]
enum EventDetail {
Detail1 { a: usize },
Detail2 { b: usize },
}
#[derive(Debug)]
struct Event {
id: usize,
detail: EventDetail,
}
fn main() {
let detail1: Event = create_detail_1();
dbg!(&detail1);
if let EventDetail::Detail1 { a } = detail1.detail {
println!("detail 1: {}", &a);
} else {
panic!("gah")
}
let detail2: Event = create_detail_2();
dbg!(&detail2);
if let EventDetail::Detail2 { b } = detail2.detail {
println!("detail 2: {}", &b);
} else {
panic!("gah")
}
}
fn create_detail_1() -> Event {
Event {
id: 1,
detail: EventDetail::Detail1 { a: 1 },
}
}
fn create_detail_2() -> Event {
Event {
id: 2,
detail: EventDetail::Detail2 { b: 2 },
}
}
On the other hand, I could use generics, which (I think) won't allow me exhaustive matching, but then again I won't need it because dispatch is based on the type of detail
.
This provides a much nicer development experience.
trait Detail {}
#[derive(Debug)]
struct Event<T: Detail> {
id: usize,
detail: T,
}
#[derive(Debug)]
struct Detail1 {
a: usize,
}
impl Detail for Detail1 {}
#[derive(Debug)]
struct Detail2 {
b: usize,
}
impl Detail for Detail2 {}
fn main() {
let detail1: Event<Detail1> = create_detail_1();
println!("detail 1: {}", &detail1.detail.a);
dbg!(&detail1);
let detail2: Event<Detail2> = create_detail_2();
dbg!(&detail2);
println!("detail 2: {}", &detail2.detail.b);
}
fn create_detail_1() -> Event<Detail1> {
Event {
id: 1,
detail: Detail1 { a: 1 },
}
}
fn create_detail_2() -> Event<Detail2> {
Event {
id: 2,
detail: Detail2 { b: 2 },
}
}
My question is - enums seem to be preferred over generics for modelling this sort of thing, but they are much more painful when there are a lot of different types (I have hundreds of different types of events).
What are your thoughts?