Reputation: 25
I have an enum that has Default
derived on it, say for example:
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum Enum<T> {
#[default] None,
One(T),
Two(T),
}
Vec
has methods like last()
which return an Option<&T>
. But when I call unwrap_or_default()
on the Option<&T>
, I get the following error:
22 | println!("{:?}", v.last().unwrap_or_default());
| ^^^^^^^^ ----------------- required by a bound introduced by this call
| |
| the trait `Default` is not implemented for `&Enum<i8>`
|
= help: the trait `Default` is implemented for `Enum<T>`
note: required by a bound in `Option::<T>::unwrap_or_default`
I can work around this in one of two ways:
unwrap_or()
and provide a reference to the default element manually:
v.last().unwrap_or(&Enum::None)
&Enum
.
impl<'a> Default for &'a Enum2 {
fn default() -> &'a Enum2 { &Enum2::None }
}
But I don't realize why I should have to. Is there a separate trait I should derive to use the tagged default element correctly here?
Context: the goal was to output "None" if the list was empty and Enum::Display(val)
from Some(val)
for the last element, within a sequence of format
arguments.
match self.history.last() { None => "None", Some(val) => val }
is illegal: the types of the match
arms must match. Many other options don't work due to val
being borrowed and self
not being Copy
. Defining a separate default for Enum
was an easily-thought-of option to avoid extra string formatting/allocation.
Upvotes: 0
Views: 1484
Reputation: 22748
What should Default
return for &Enum
?
Remember, a reference must point to some valid data, and does not keep said data alive.
So even if the Default
implementation would allocate a new object, there is nowhere to keep it alive, because the returning reference wouldn't keep it alive. The only way is to store the default element somewhere globally; and then it would be shared for everyone. Many objects are more complicated and cannot be simply stored globally, because they are not const-initializable. What should happen then?
The fact that a reference does not implement Default
automatically is intentional and with good reasons. unwrap_or_default
is simply the wrong function to call if your contained value is a reference. Use match
or if let
to extract the value without having to create a fallback value. unwrap_or
is also a valid choice if you must absolutely have a fallback value, although I don't see how this is easily possible to implement with a reference without falling back to global const values. Which is also fine, if it fits your usecase.
In your case, it's of course fine to derive Default
manually and is probably what I would do if I absolutely had to. But to me, requiring this in the first place is most likely a code smell in the first place.
For enums that are that simple, it's actually an anti-pattern to use a non-mutable reference instead of a value. References are of size 64
in a 64bit system, while an enum of with three simple values is most likely a 8
bits large. So using a reference is an overhead.
So in this case, you can use copied()
to query the object by-value. This is most likely even faster than querying it by reference.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum Enum<T> {
#[default]
None,
One(T),
Two(T),
}
fn main() {
let v: Vec<Enum<u32>> = vec![];
println!("{:?}", v.last().copied().unwrap_or_default());
}
None
Either way, I think the fact that you have this question in the first place indicates that there is an architectural issue in your code.
There are two cases:
.last()
between "one element with some data" and "no element", and falling back to a default wouldn't be viable.None
member, in which case having the None
element at .last()
would be identical to having no members. (which is what I suspect your code does). In that case it's pointless to manually define a None
member, though, simply use the Option
enum that already exists. That would make your problem trivially solvable; Option
already implements Default
(which is None
) and can propagate the reference to its member via .as_ref()
:#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EnumContent<T> {
One(T),
Two(T),
}
// Option already implements `Default`.
pub type Enum<T> = Option<EnumContent<T>>;
fn main() {
let mut v: Vec<Enum<u32>> = vec![];
println!("{:?} -> {:?}", v, v.last().and_then(Option::as_ref));
v.push(Some(EnumContent::One(42)));
println!("{:?} -> {:?}", v, v.last().and_then(Option::as_ref));
v.push(None);
println!("{:?} -> {:?}", v, v.last().and_then(Option::as_ref));
}
[] -> None
[Some(One(42))] -> Some(One(42))
[Some(One(42)), None] -> None
Upvotes: 2