Let's say I write a module that (simplified for this example here) reads out an EEPROM. Some values there are stored as integer, some as string. In my program code I need some values as integer (to use them for calculation), some of them as string, e.g. for displaying them to the user.
So it would make code easy if I wouldn't have to care for return value type.
Rust supports return value polymorphism by using traits - so why not give it a try?
fn main() {
let my_mod = MyMod::new();
let value_u32: u32 = my_mod.my_get().unwrap();
let value_string: String = my_mod.my_get().unwrap();
println!("Values: {}, {}", value_u32, value_string);
}
trait SupportsMyGet<T> {
fn my_get(&self) -> Result<T, String>;
}
struct MyMod {}
impl MyMod {
pub fn new() -> MyMod {
MyMod {}
}
}
impl SupportsMyGet<u32> for MyMod {
fn my_get(&self) -> Result<u32, String> {
Ok(42)
}
}
impl SupportsMyGet<String> for MyMod {
fn my_get(&self) -> Result<String, String> {
let r: Result<u32, String> = self.my_get();
r.map(|v| format!("{}", v))
}
}
Okay great, this does what I want to do.
Oh okay, I don't know at compile time which implementation to use - this is decided at runtime? How bad. But yeah, there's something in the box for it - let's Box an object that implements my trait and decide at runtime, which object to pass:
fn main() {
let my_mod = get_mod_to_use().unwrap();
let value_u32: u32 = my_mod.my_get().unwrap();
let value_string: String = my_mod.my_get().unwrap();
println!("Values: {}, {}", value_u32, value_string);
}
fn get_mod_to_use<T>() -> Result<Box<dyn SupportsMyGet<T>>, String> {
Ok(Box::new(MyMod::new()))
}
But that doesn't work out well:
error[E0308]: mismatched types
--> src/main.rs:4:32
|
4 | let value_string: String = my_mod.my_get().unwrap();
| ------ ^^^^^^^^^^^^^^^^^^^^^^^^- help: try using a conversion method: `.to_string()`
| | |
| | expected struct `String`, found `u32`
| expected due to this
error[E0277]: the trait bound `MyMod: SupportsMyGet<T>` is not satisfied
--> src/main.rs:10:8
|
10 | Ok(Box::new(MyMod::new()))
| -- ^^^^^^^^^^^^^^^^^^^^^^ the trait `SupportsMyGet<T>` is not implemented for `MyMod`
| |
| required by a bound introduced by this call
|
= note: required for the cast to the object type `dyn SupportsMyGet<T>`
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
9 | fn get_mod_to_use<T>() -> Result<Box<dyn SupportsMyGet<T>>, String> where MyMod: SupportsMyGet<T> {
| +++++++++++++++++++++++++++++
Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
error: could not compile due to 2 previous errors
return value polymorphism is gone... because Rust wants to box a specific object. Leaving out the generic argument also doesn't work as the trait requires it.
So how can this still be done?