I'm not very experienced yet, so my proposal might not be idiomatic, but what about creating a trait to cover both cases?
If you have
struct IntervalOpt {
interval: Option<u64>,
id: Option<String>,
}
struct Interval {
interval: u64,
id: String,
}
you could define a common trait
trait IntervalGeneric {
fn interval(&self) -> Option<u64>;
fn id(&self) -> Option<&String>;
}
because both types allow you to retrieve an Option<u64>
for the interval and an Option<&String>
for the ID.
The trait could be implemented as follows:
impl IntervalGeneric for IntervalOpt {
fn interval(&self) -> Option<u64> {
self.interval
}
fn id(&self) -> Option<&String> {
self.id.as_ref()
}
}
impl IntervalGeneric for Interval {
fn interval(&self) -> Option<u64> {
Some(self.interval)
}
fn id(&self) -> Option<&String> {
Some(&self.id)
}
}
Then you can have functions that deal with both cases, such as:
fn print_interval_generic<T: IntervalGeneric>(value: T) {
println!(
"Interval = {}, ID = {}",
value.interval().map_or("NONE".to_string(), |x| x.to_string()),
value.id().map_or("NONE", |x| &x),
);
}
fn main() {
let v1 = Interval {
interval: 1,
id: "101".to_string(),
};
let v2 = IntervalOpt {
interval: Some(10),
id: Some("102".to_string()),
};
let v3 = IntervalOpt {
interval: None,
id: Some("103".to_string()),
};
print_interval_generic(v1);
print_interval_generic(v2);
print_interval_generic(v3);
}
(Note: I don't like the "NONE".to_string()
there, as it's causing an unnecessary allocation in many cases, but this is just an example.)
But as I said, not sure if that's the best approach. Maybe it also depends on how your API is overall defined and designed.