ncts
July 12, 2024, 2:25pm
1
With these types:
struct Data {
value: u64,
}
impl Data {
fn try_clone(&self) -> Result<u64> {
Ok(Self { value: self.value })
}
}
enum Thing {
Foo(Data),
Bar,
}
Currently I do:
let cloned = if let Thing::Foo(foo) = thing {
Some(foo)
} else {
None
}
.and_then(|foo| foo.try_clone().ok())
.ok_or_else(|| MyError("invalid foo data"))?;
Is there a more idiomatic way to do that? Thanks
If you want a shorthand, you can add a method to your enum, for example:
impl Thing {
// NOTE: You might want a `self` or `&mut self` version instead.
fn as_foo(&self) -> Option<&Data> {
match self {
Self::Foo(data) => Some(data),
_ => None
}
}
}
and use it like this:
let cloned = Thing::Foo(foo)
.as_foo()
.and_then(|foo| foo.try_clone().ok())
.ok_or_else(|| MyError("invalid foo data"))?;
Or you could use a macro like the ones in the try_match crate.
1 Like
ncts
July 12, 2024, 2:43pm
3
Thanks for the detailed reply! Unfortunately the enum is from an external crate, so I can't impl it (maybe through an ad-hoc trait but that feels a bit ugly). The crate seems interesting, will try it out
I think the .and_then
callback can be moved just into the first branch:
let cloned = if let Thing::Foo(foo) = thing {
foo.try_clone().ok()
} else {
None
}.ok_or_else(|| MyError("invalid foo data"))?;
1 Like
Extension traits are a standard pattern. There's nothing ugly about them, they are just regular traits.
ncts
July 14, 2024, 1:30pm
6
Indeed. Maybe not elegant enough? Anyway, here's my attempt:
trait EnumIntoOption {
type Value;
fn into_option(self) -> Option<Self::Value>;
}
impl EnumIntoOption for Thing {
type Value = Data;
fn into_option(self) -> Option<Self::Value> {
match self {
Self::Foo(data) => Some(data),
_ => None,
}
}
}
system
Closed
October 12, 2024, 1:31pm
7
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.