I'm trying to write a generic command line parser. I'm having trouble with "default" values for generic types. cmd.invoke()
returns a Result<K, E>
, so there's no problem there, but how do I represent a placeholder or default value for E
when cmd_to_invoke
is a None
? In C#, I could use default(E)
. Is there a construct like this in Rust?
pub struct Cmd<K, E> {
cmds: Vec<Cmd<K, E>>,
}
impl<K, E> Cmd<K, E> {
pub fn invoke(&mut self, cmd_name: &str) -> Result<K, E> {
let cmd_to_invoke = self.cmds.iter_mut().find(|cmd| cmd.name == cmd_name);
if let Some(cmd) = cmd_to_invoke {
cmd.invoke()
} else {
// Some default / placeholder value for E
Err(/* ? */)
}
}
}
In Rust, generics refer to the parameterization of data types and traits. Generics allows to write more concise and clean code by reducing code duplication and providing type-safety. The concept of Generics can be applied to methods, functions, structures, enumerations, collections and traits.
GATs (generic associated types) were originally proposed in RFC 1598. As said before, they allow you to define type, lifetime, or const generics on associated types. If you're familiar with languages that have "higher-kinded types", then you could call GATs type constructors on traits.
You probably are looking for Default
.
For example:
pub struct Cmd<K, E> {}
impl<K, E: Default> Cmd<K, E> {
pub fn invoke(&mut self, cmd_name: &str) -> Result<K, E> {
let cmd_to_invoke = self.cmds.iter_mut().find(|cmd| cmd.name == cmd_name);
if let Some(cmd) = cmd_to_invoke {
cmd.invoke()
} else {
// Some default / placeholder value for E
Err(Default::default())
}
}
}
It's worth noting though that most error types in std
and popular crates do not implement Default
.
The idiomatic way of dealing with multiple error types in Rust is to define your own error enum
for your application, library or component, which implements From
for each possible underlying error type. This generally makes code simpler and easier to read, and works very nicely with the ?
operator. Here is an example.
If you are writing a library which really must work with any error type, then this won't work. In that case, you are probably left with constraining E
to the std::error::Error
trait and then making a custom error type which can be converted from that, possibly keeping reference to the underlying error as a trait object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With