Why does Default derived on an enum not apply to references to the enum? - rust

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:
Use unwrap_or() and provide a reference to the default element manually:
v.last().unwrap_or(&Enum::None)
Implement Default for &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?
Playground
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.

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:
Your enum members all contain valid data; in that case it is always important to distinguish in .last() between "one element with some data" and "no element", and falling back to a default wouldn't be viable.
Your enum has a 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

Related

How to understand closure parameters

I am a beginner in Rust. I tried to write a linked list for practice.
Some sturct code:
type Link<T> = Option<Box<Node<T>>>;
pub struct List<T> {
head: Link<T>,
}
struct Node<T> {
elem: T,
next: Link<T>,
}
Peek function for List:
error code:
pub fn peek(&self) -> Option<&T> {
self.head.map(|ref node| {
&node.elem
})
}
The Rust compiler return: cannot return value referencing function parameter.
The following code is ok:
pub fn peek(&self) -> Option<&T> {
self.head.as_ref().map(|node| {
&node.elem
})
}
So I guess,
The error code is actually an abbreviation of the following code:
self.head.map(|node| {
let ref ref_node = node;
&ref_node.elem
})
Is my guess correct?
More, is there a similar abbreviation in Rust? Or abbreviation rules.
The error “cannot return value referencing function parameter” is sort of a red herring here; it has nothing to do with the fundamental problem, but just happened to be listed first. If you compile the code
type Link<T> = Option<Box<Node<T>>>;
pub struct List<T> {
head: Link<T>,
}
struct Node<T> {
elem: T,
next: Link<T>,
}
impl<T> List<T> {
pub fn peek(&self) -> Option<&T> {
self.head.map(|ref node| &node.elem)
}
}
then you will get two errors, the second of which is the reason this code doesn't work and you needed .as_ref():
error[E0507]: cannot move out of `self.head` which is behind a shared reference
--> src/lib.rs:14:9
|
14 | self.head.map(|ref node| &node.elem)
| ^^^^^^^^^ move occurs because `self.head` has type `Option<Box<Node<T>>>`, which does not implement the `Copy` trait
|
help: consider borrowing the `Option`'s content
|
14 | self.head.as_ref().map(|ref node| &node.elem)
| +++++++++
This error is the one from the root cause; Option::map consumes its input Option, which is not what we want to do here. Option::as_ref solves this by taking an &Option<T> (here, by implicit auto-reference turning self.head into &self.head) and returning a newly created Option<&T>, which can then be consumed by Option::map.
But, I understand that you want to know how the erroneous code produced the “cannot return…” error. Here's how:
We know that self.head is an Option<Box<Node<T>>>.
Calling Option::map() on that type means it expects a function FnOnce(Box<Node<T>>) -> &T.
That is, the closure is acting as a function which is given ownership of a Box<Node<T>>. So, within the closure, node is a local variable that owns the Box.
The body of the closure then attempts to return a reference to a field of the Node inside that box, which is an error E0515 since the box is owned by a local variable and is going to be dropped at the end of the function.
Your ref modifier does not change things, because whether a value is moved into a function is determined by the function's parameter types, not by what kind of pattern/binding the function body uses.
The error code is actually an abbreviation of the following code:
self.head.map(|node| {
let ref ref_node = node;
&ref_node.elem
})
Is my guess correct?
Yes and no. You can expand the code that way. But that's not the “real” code; it's rather that introducing the separate ordinary variable node before a more complex pattern is always valid, for function parameters. It's not something that the compiler does anyway; it's adding trivial code, in the same way that multiplying by 1 doesn't change a number. But, the fact that you can do this without changing the program is a consequence of the fact that function parameters are moved in regardless of what the function itself writes in its parameter patterns.
However, it wouldn't be possible to make your original code work by changing Rust function parameters to work that way. The function which is causing the problem is Option::map() applied to a borrowed struct field; that would always fail regardless of what the function passed to map() does, because Option::map() always consumes the Option given to it.

Is it possible to assign the return value of different functions that return different structs (that implement a common trait) to a single variable?

Let's say I have something like this:
trait SomeTrait {}
struct One;
impl SomeTrait for One {}
struct Two;
impl SomeTrait for Two {}
fn return_one() -> One {
One
}
fn return_two() -> Two {
Two
}
Somewhere else, I want to essentially do:
fn do_stuff(op: Operation) {
let result = match op {
Operation::OpOne => return_one(),
Operation::OpTwo => return_two(),
};
}
That of course doesn't compile, as those two return_*() functions return distinct types. I've tried:
Declaring result as dyn SomeTrait (still error: mismatched types)
Casting the return values, e.g. return_one() as dyn SomeTrait (error: cast to unsized type: One as dyn SomeTrait)
Making Sized a supertrait of SomeTrait (this won't work for me in this particular case as I don't have control over the real-world version of SomeTrait, but it doesn't compile anyway: error: the trait SomeTrait cannot be made into an object
Things I think would work but don't want to or can't do:
Boxing values on return, e.g. Box::new(return_one()) as dyn Box<SomeTrait> (having to move the values into a box, and thus off the stack, seems excessive)
Having return_one() and return_two() instead return impl SomeTrait (this would allow me to accidentally return Two from return_one(), for example, and I want to use the type system to prevent that)
Wrapping with an enum: I don't want the functions to return a wrapper enum, because then we have the same problem as the previous bullet point. I could wrap the return values in an enum at the call site, and that could work, but let's say there's some function on SomeTrait that I want to call at the end; it seems like a lot of extra boilerplate to then unwrap the enum and call that function for each inner type. If I were to do that, I might as well just copy-paste the trait function call to each match arm.
I found a few crates on crates.io that claim to be able to do this, but AFAICT they all require implementing a trait on the types, which are foreign types for me, so I can't do that.
Is it possible to make this work?
A possible option is to do the following
fn do_stuff(op: Operation) {
let (one, two);
let _result: &dyn SomeTrait = match op {
Operation::OpOne => {one = return_one(); &one},
Operation::OpTwo => {two = return_two(); &two},
};
}
You can also use &mut dyn SomeTrait instead if you need to borrow it mutably.
This is somewhat verbose, but if you find yourself doing it a lot, a macro
that declares the anonymous variables, assigns them and returns a reference might help.
Another solution could be to use the auto_enums crate, which automaticaly creates the enum and implements the trait for it, the downside is that it only supports certain traits, (mostly in std, I believe) and that for this specific use case it requires nightly, or putting the match in a separate function.
I'm not sure I can link a specific part of the docs, but if you scroll down to "#Rust Nightly", you'll see your specific use of it, something like as follows
use auto_enums::auto_enum;
fn do_stuff(op: Operation) {
#[auto_enum(SomeTrait)]
let _result = match op {
Operation::OpOne => return_one(),
Operation::OpTwo => return_two(),
};
}
Although keep in mind this only works if auto_enums supports SomeTrait.

How to assign an impl trait in a struct?

Consider some struct (HiddenInaccessibleStruct) that is not accessible, but implements an API trait. The only way to obtain an object of this hidden type is by calling a function, that returns an opaque implementation of this type. Another struct owns some type, that makes use of this API trait. Right now, it seems not possible to assign this field in fn new(). The code below can also be found in rust playgrounds.
// -- public api
trait Bound {
fn call(&self) -> Self;
}
// this is not visible
#[derive(Default)]
struct HiddenInaccessibleStruct;
impl Bound for HiddenInaccessibleStruct {
fn call(&self) -> Self { }
}
// -- public api
pub fn load() -> impl Bound {
HiddenInaccessibleStruct::default()
}
struct Abc<T> where T : Bound {
field : T
}
impl<T> Abc<T> where T : Bound {
pub fn new() -> Self {
let field = load();
Abc {
field // this won't work, since `field` has an opaque type.
}
}
}
Update
The API trait Bound declares a function, that returns Self, hence it is not Sized.
There are two concepts in mid-air collision here: Universal types and existential types. An Abc<T> is a universal type and we, including Abc, can refer to whatever T actually is as T (simple as that). impl Trait-types are Rust's closest approach to existential types, where we only promise that such a type exists, but we can't refer to it (there is no T which holds the solution). This also means your constructor can't actually create a Abc<T>, because it can't decide what T is. Also see this article.
One solution is to kick the problem upstairs: Change the constructor to take a T from the outside, and pass the value into it:
impl<T> Abc<T>
where
T: Bound,
{
pub fn new(field: T) -> Self {
Abc { field }
}
}
fn main() {
let field = load();
let abc = Abc::new(field);
}
See this playground.
This works, but it only shifts the problem: The type of abc in main() is Abc<impl Bound>, which is (currently) impossible to write down. If you change the line to let abc: () = ..., the compiler will complain that you are trying to assign Abc<impl Bound> to (). If you try to comply with the advice and change the line to let abc: Abc<impl Bound> = ..., the compiler will complain that this type is invalid. So you have to leave the type of abc being implied. This brings some useability issues with Abc<impl Bound>, because you can't easily put values of that type into other structs etc.; basically, the existential type "infects" the outer type containing it.
impl Trait-types are mostly useful for immediate consumption, e.g. impl Iterator<Item=...>. In your case, with the aim apparently being to hide the type, you may get away with sealing Bound. In a more general case, it may be better to use dynamic dispatch (Box<dyn Bound>).

How to move a field whose type does not implement Default from a struct which implements Drop?

There are some similar questions, but the answers require the field to implement Default or some way to initialize another value with the type of the field.
We have a Node which has a value of type T:
struct Node<T> {
value: T,
next: Option<Box<T>>
}
It has a method which moves value from a Node:
impl<T> Node<T> {
fn value(self) -> T {
self.value
}
}
The code above compiles. But if we implement Drop for Node:
impl<T> Drop for Node<T> {
fn drop(&mut self) {}
}
Then we will get a compile error:
error[E0509]: cannot move out of type `Node<T>`, which implements the `Drop` trait
| self.value
| ^^^^^^^^^^
| |
| cannot move out of here
| move occurs because `self.value` has type `T`, which does not implement the `Copy` trait
I guess it doesn't compile because if we implement a custom Drop, we need to make sure not to drop value field if the drop happens at the end of value method's block. However, we cannot check that; and even if we could, the compiler cannot statically check that we do it.
One way to workaround this is to store value field as Option<T>. But suppose that we don't want to use Option for some reasons (the overhead, etc),
What else can we do to have both a custom Drop and a value method that moves value field?
I guess we have to use some unsafe approach and that's fine.
Rust Playground
I don't know of a way to do this without using unsafe (though someone else might), but here is a way to do it using unsafe:
use std::{ptr, mem};
impl<T> Node<T> {
fn value(mut self) -> T {
unsafe {
let v: T = ptr::read(&self.value);
ptr::drop_in_place(&mut self.next);
mem::forget(self);
v
}
}
}
We use ptr::read to move the desired value out. We then need to use mem::forget on the Node to make sure its drop method is not called (since otherwise value could be dropped twice and cause undefined behavior). To prevent the next member from leaking, we use ptr::drop_in_place to run its drop method.
It's interesting to me that this safe code does not work:
impl<T> Node<T> {
fn value(self) -> T {
match self {
Node {value, next: _} => value
}
}
}
It gives that same error:
error[E0509]: cannot move out of type Node<T>, which implements the
Drop trait
I would have expected that with the match expression taking ownership of all of self and breaking it into its components, there would be no way for drop to be called on self and hence no reason for the compiler to complain. But apparently it does not work this way.

How to Box a trait that has associated types?

I'm very new to Rust so I may have terminology confused.
I want to use the hashes crates to do some hashing and I want to dynamically pick which algorithm (sha256, sha512, etc.) to use at runtime.
I'd like to write something like this:
let hasher = match "one of the algorithms" {
"sha256" => Box::new(Sha256::new()) as Box<Digest>,
"sha512" => Box::new(Sha512::new()) as Box<Digest>
// etc...
};
I sort of get that that doesn't work because the associated types required by Digest aren't specified. If I attempt to fill them in:
"sha256" => Box::new(Sha256::new()) as Box<Digest<<OutputSize = U32, BlockSize = U64>>>,
I'm left with an error: the trait 'digest::Digest' cannot be made into an object. I think this approach will fail anyway because match will be returning slightly different types in cases where different algorithms have different associated types.
Am I missing something obvious? How can I dynamically create an instance of something that implements a trait and then hold on to that thing and use it through the trait interface?
The message refers to object safety (longer article). The Digest trait has two incompatibilities:
It uses associated types (this can be worked around by explicitly setting all type parameters to values compatible for all Digest objects).
It has a method (fn result(self) -> …) taking self by value. You won't be able to call it, which ruins usability of this trait.
Once a trait object is created, information about its subtype-specific features such as memory layout or associated types is erased. All calls to the trait object's methods are done via a vtable pointer. This means they all must be compatible, and Rust can't allow you to call any methods that could vary in these aspects.
A workaround is to create your custom wrapper trait/adapter that is object-compatible. I'm not sure if that's the best implementation, but it does work:
trait Digest {
type Assoc;
fn result(self);
}
struct Sha;
impl Digest for Sha {
type Assoc = u8;
fn result(self) {}
}
///////////////////////////////////////////
trait MyWrapper {
fn result(&mut self); // can't be self/Sized
}
impl<T: Digest> MyWrapper for Option<T> {
fn result(&mut self) {
// Option::take() gives owned from non-owned
self.take().unwrap().result()
}
}
fn main() {
let mut digest: Box<MyWrapper> = Box::new(Some(Sha));
digest.result();
}

Resources