In a match, expected reference, found enum, error E0308 [duplicate] - enums

It seems like every introductory document for Rust's enum types explains how to match on an enum object that you own, but what if you do not own the enum object and you just have a reference to it that you want to match against? I don't know what the syntax would be.
Here is some code where I attempt to match on a reference to an enum:
use std::fmt;
use std::io::prelude::*;
pub enum Animal {
Cat(String),
Dog,
}
impl fmt::Display for Animal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Animal::Cat(c) => f.write_str("c"),
Animal::Dog => f.write_str("d"),
}
}
}
fn main() {
let p: Animal = Animal::Cat("whiskers".to_owned());
println!("{}", p);
}
The Rust Playground gives errors on the first two cases of the match when trying to compile it:
error[E0308]: mismatched types
--> src/main.rs:12:13
|
12 | Animal::Cat(c) => f.write_str("c"),
| ^^^^^^^^^^^^^^ expected &Animal, found enum `Animal`
|
= note: expected type `&Animal`
= note: found type `Animal`
error[E0308]: mismatched types
--> src/main.rs:13:13
|
13 | Animal::Dog => f.write_str("d"),
| ^^^^^^^^^^^ expected &Animal, found enum `Animal`
|
= note: expected type `&Animal`
= note: found type `Animal`
How can I change that code to get it to compile? I tried adding ampersands in lots of different places without any luck. Is it even possible to match on a reference to an enum?

As of Rust 1.26, the idiomatic way is the way that you originally wrote it because match ergonomics have been improved:
use std::fmt;
pub enum Animal {
Cat(String),
Dog,
}
impl fmt::Display for Animal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Animal::Cat(_) => f.write_str("c"),
Animal::Dog => f.write_str("d"),
}
}
}
fn main() {
let p: Animal = Animal::Cat("whiskers".to_owned());
println!("{}", p);
}

Edit: Please see Shepmaster's answer for the latest idiom
The idiomatic way would be
match *self {
Animal::Cat(ref c) => f.write_str("c"),
Animal::Dog => f.write_str("d"),
}
You can use _ instead of ref c to silence the "unused" warning.

I figured it out thanks to helpful compiler messages:
match self {
&Animal::Cat(ref c) => f.write_str("c"),
&Animal::Dog => f.write_str("d"),
}

Related

How do I filter a vector of an enum of different structs using a common field?

I found you can create a vector of different types of structs using an enum. When filtering the vector on a common field, such as id, the compiler doesn't know the type while iterating:
use chrono::{DateTime, Utc}; // 0.4.19
use serde::{Serialize, Deserialize}; // 1.0.126
#[derive(Deserialize, Debug, Serialize, Clone)]
pub enum TransactionsEnum {
TransactionOrderA(TransactionOrderA),
TransactionOrderB(TransactionOrderB),
}
#[derive(Deserialize, Debug, Serialize, Clone)]
pub struct TransactionOrderA {
pub id: i64,
pub accountID: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TransactionOrderB {
pub id: i64,
pub time: DateTime<Utc>,
}
fn transactions_filter(
transactions_vector: Vec<TransactionsEnum>,
x: i64,
) -> Vec<TransactionsEnum> {
transactions_vector
.into_iter()
.filter(|e| e.id >= x)
.collect()
}
error[E0609]: no field `id` on type `&TransactionsEnum`
--> src/lib.rs:27:23
|
27 | .filter(|e| e.id >= x)
| ^^
Sharing a common value in all enum values and Is there a way to directly access a field value in an enum struct without pattern matching? indirectly answer my question, but the answer provided here helped me understand why a match statement is necessary.
Those are not a common field, they are completely unrelated fields. The fact that they share a name is, as far as the Rust compiler is concerned, an insignificant coincidence. You need to use pattern matching to get the field from either case
impl TransactionsEnum {
pub fn id(&self) -> i64 {
match self {
TransactionsEnum::TransactionOrderA(value) => value.id,
TransactionsEnum::TransactionOrderB(value) => value.id,
}
}
}
transactions_vector
.into_iter()
.filter(|e| e.id() >= x) // Note the parentheses since we're calling a function now
.collect()

How to tell the compiler the variant of the enum I'm returning always has no lifetime?

The below code won't compile because the compiler thinks I shouldn't assign to t1 since it is borrowed, but in reality the function always_returns_no_lifetime will always be returning a variant of the enum that actually doesn't have an lifetime, so it is okay for me to modify t1. How can I get the compiler to understand this or how should I reorganize my code to make this error not happen?
#[derive(Clone)]
enum Types<'a> {
NoLifetime(i32),
AlsoNoLifetime(i32),
AlsoAlsoNoLifetime(i32),
HasLifetime(&'a str)
}
fn always_returns_no_lifetime<'a>(some_type: &'a Types) -> Types<'a> {
match *some_type {
Types::HasLifetime(text) => panic!("I only return the type that has no lifetime"),
_ => some_type.clone()
}
}
fn main() {
let mut t1 = Types::NoLifetime(20);
let copy = always_returns_no_lifetime(&t1);
t1 = Types::NoLifetime(30);
}
playground
The error is:
error[E0506]: cannot assign to `t1` because it is borrowed
--> src/main.rs:23:5
|
21 | let copy = always_returns_no_lifetime(&t1);
| -- borrow of `t1` occurs here
22 |
23 | t1 = Types::NoLifetime(30);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `t1` occurs here
The return type of your function is wrong. If the return value is guaranteed not to have any lifetime, then it should be marked as such, and not tied down to an arbitrary lifetime:
fn always_returns_no_lifetime(...) -> Types<'static>;
With this change, you actually no longer need any input lifetime either, since they are only useful to tie the input and output, leading the following signature:
fn always_returns_no_lifetime(some_type: &Types) -> Types<'static>;
Unfortunately, this means that clone is now out of the table, as it clones the lifetime, so the implementation has to change too:
fn always_returns_no_lifetime(some_type: &Types) -> Types<'static> {
match *some_type {
Types::HasLifetime(_)
=> panic!("I only return values that have no lifetime"),
Types::NoLifetime(i) => Types::NoLifetime(i),
Types::AlsoNoLifetime(i) => Types::AlsoNoLifetime(i),
Types::AlsoAlsoNoLifetime(i) => Types::AlsoAlsoNoLifetime(i),
}
}
The benefit of this implementation can be demonstrated in the following example:
fn tie<'a>(text: &'a str) -> Types<'a> {
if text[0] == 'a' {
Types::HasLifetime(text)
} else {
Types::NoLifetime(0)
}
}
fn main() {
let no_lifetime = {
let string = String::from("Hello, world");
let has_lifetime = tie(&*string);
always_returns_no_lifetime(&has_lifetime)
};
// Requires deriving Debug, all structs really should...
println!("{:?}", no_lifetime);
}
If you preserve the lifetime when you don't need it, you cannot compile this example, it's an unnecessary restriction.
Instead of applying the 'a lifetime parameter on the reference, apply it on Types, as you already did with the return type. The lifetime on the reference is not important when you call .clone() on it.
fn always_returns_no_lifetime<'a>(some_type: &Types<'a>) -> Types<'a> {
match *some_type {
Types::HasLifetime(text) => panic!("I only return the type that has no lifetime"),
_ => some_type.clone()
}
}

"Expected type parameter, found reference to type parameter" when implementing a generic cache struct

In the Closures chapter of the second edition of The Rust Programming Language, the writer implements a Cache struct and leaves it with a few problems for the reader to fix up, such as:
Accepting generic parameters and return values on the closure function
Allowing more than one value to be cached
I've attempted to fix those problems but I am quite stuck and can't make it work.
use std::collections::HashMap;
use std::hash::Hash;
struct Cacher<T, X, Y>
where
T: Fn(&X) -> &Y,
X: Eq + Hash,
{
calculation: T,
results: HashMap<X, Y>,
}
impl<T, X, Y> Cacher<T, X, Y>
where
T: Fn(&X) -> &Y,
X: Eq + Hash,
{
fn new(calculation: T) -> Cacher<T, X, Y> {
Cacher {
calculation,
results: HashMap::new(),
}
}
fn value<'a>(&'a mut self, arg: &'a X) -> &'a Y {
match self.results.get(arg) {
Some(v) => v,
None => {
let res = (self.calculation)(arg);
self.results.insert(*arg, res);
res
}
}
}
}
Where T is the closure function type, X is the argument type and Y is the return value type.
The error I get:
error[E0308]: mismatched types
--> src/main.rs:30:43
|
30 | self.results.insert(*arg, res);
| ^^^ expected type parameter, found &Y
|
= note: expected type `Y`
found type `&Y`
I understand this, but I can't think of an elegant solution for the whole ordeal.
You've stated that your closure returns a reference:
T: Fn(&X) -> &Y,
but then you are trying to store something that isn't a reference:
results: HashMap<X, Y>,
This is fundamentally incompatible; you need to unify the types.
In many cases, there's no reason to have a reference to a generic type because a generic type can already be a reference. Additionally, forcing the closure to return a reference means that a closure like |_| 42 would not be valid. Because of that, I'd say you should return and store the value type.
Next you need to apply similar logic to value, as it needs to take the argument by value in order to store it. Additionally, remove all the lifetimes from it as elision does the right thing: fn value(&mut self, arg: X) -> &Y.
Once you've straightened that out, apply the knowledge from How to lookup from and insert into a HashMap efficiently?:
fn value(&mut self, arg: X) -> &Y {
match self.results.entry(arg) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
let res = (self.calculation)(e.key());
e.insert(res)
}
}
}
Round it off with some tests that assert it's only called once, and you are good to go. Note that we had to make decisions along the way, but they aren't the only ones we could have chosen. For example, we could have made it so that the cached value is cloned when returned.
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::Hash;
struct Cacher<F, I, O>
where
F: Fn(&I) -> O,
I: Eq + Hash,
{
calculation: F,
results: HashMap<I, O>,
}
impl<F, I, O> Cacher<F, I, O>
where
F: Fn(&I) -> O,
I: Eq + Hash,
{
fn new(calculation: F) -> Self {
Cacher {
calculation,
results: HashMap::new(),
}
}
fn value(&mut self, arg: I) -> &O {
match self.results.entry(arg) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
let res = (self.calculation)(e.key());
e.insert(res)
}
}
}
}
#[test]
fn called_once() {
use std::sync::atomic::{AtomicUsize, Ordering};
let calls = AtomicUsize::new(0);
let mut c = Cacher::new(|&()| {
calls.fetch_add(1, Ordering::SeqCst);
()
});
c.value(());
c.value(());
c.value(());
assert_eq!(1, calls.load(Ordering::SeqCst));
}

How to match enum variants dynamically in Rust? [duplicate]

I have an enum with the following structure:
enum Expression {
Add(Add),
Mul(Mul),
Var(Var),
Coeff(Coeff)
}
where the 'members' of each variant are structs.
Now I want to compare if two enums have the same variant. So if I have
let a = Expression::Add({something});
let b = Expression::Add({somethingelse});
cmpvariant(a, b) should be true. I can imagine a simple double match code that goes through all the options for both enum instances. However, I am looking for a fancier solution, if it exists. If not, is there overhead for the double match? I imagine that internally I am just comparing two ints (ideally).
As of Rust 1.21.0, you can use std::mem::discriminant:
fn variant_eq(a: &Op, b: &Op) -> bool {
std::mem::discriminant(a) == std::mem::discriminant(b)
}
This is nice because it can be very generic:
fn variant_eq<T>(a: &T, b: &T) -> bool {
std::mem::discriminant(a) == std::mem::discriminant(b)
}
Before Rust 1.21.0, I'd match on the tuple of both arguments and ignore the contents of the tuple with _ or ..:
struct Add(u8);
struct Sub(u8);
enum Op {
Add(Add),
Sub(Sub),
}
fn variant_eq(a: &Op, b: &Op) -> bool {
match (a, b) {
(&Op::Add(..), &Op::Add(..)) => true,
(&Op::Sub(..), &Op::Sub(..)) => true,
_ => false,
}
}
fn main() {
let a = Op::Add(Add(42));
let b = Op::Add(Add(42));
let c = Op::Add(Add(21));
let d = Op::Sub(Sub(42));
println!("{}", variant_eq(&a, &b));
println!("{}", variant_eq(&a, &c));
println!("{}", variant_eq(&a, &d));
}
I took the liberty of renaming the function though, as the components of enums are called variants, and really you are testing to see if they are equal, not comparing them (which is usually used for ordering / sorting).
For performance, let's look at the LLVM IR in generated by Rust 1.60.0 in release mode (and marking variant_eq as #[inline(never)]). The Rust Playground can show you this:
; playground::variant_eq
; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind nonlazybind readonly uwtable willreturn
define internal fastcc noundef zeroext i1 #_ZN10playground10variant_eq17hc64d59c7864eb861E(i8 %a.0.0.val, i8 %b.0.0.val) unnamed_addr #2 {
start:
%_8.not = icmp eq i8 %a.0.0.val, %b.0.0.val
ret i1 %_8.not
}
This code directly compares the variant discriminant.
If you wanted to have a macro to generate the function, something like this might be good start.
struct Add(u8);
struct Sub(u8);
macro_rules! foo {
(enum $name:ident {
$($vname:ident($inner:ty),)*
}) => {
enum $name {
$($vname($inner),)*
}
impl $name {
fn variant_eq(&self, b: &Self) -> bool {
match (self, b) {
$((&$name::$vname(..), &$name::$vname(..)) => true,)*
_ => false,
}
}
}
}
}
foo! {
enum Op {
Add(Add),
Sub(Sub),
}
}
fn main() {
let a = Op::Add(Add(42));
let b = Op::Add(Add(42));
let c = Op::Add(Add(21));
let d = Op::Sub(Sub(42));
println!("{}", Op::variant_eq(&a, &b));
println!("{}", Op::variant_eq(&a, &c));
println!("{}", Op::variant_eq(&a, &d));
}
The macro does have limitations though - all the variants need to have a single variant. Supporting unit variants, variants with more than one type, struct variants, visibility, etc are all real hard. Perhaps a procedural macro would make it a bit easier.

What types are valid for the `self` parameter of a method?

I wanted to create a method that only works where the self parameter was an Rc. I saw that I could use Box, so I thought I might try to mimic how that works:
use std::rc::Rc;
use std::sync::Arc;
struct Bar;
impl Bar {
fn consuming(self) {}
fn reference(&self) {}
fn mutable_reference(&mut self) {}
fn boxed(self: Box<Bar>) {}
fn ref_count(self: Rc<Bar>) {}
fn atomic_ref_count(self: Arc<Bar>) {}
}
fn main() {}
Yields these errors:
error[E0308]: mismatched method receiver
--> a.rs:11:18
|
11 | fn ref_count(self: Rc<Bar>) {}
| ^^^^ expected struct `Bar`, found struct `std::rc::Rc`
|
= note: expected type `Bar`
= note: found type `std::rc::Rc<Bar>`
error[E0308]: mismatched method receiver
--> a.rs:12:25
|
12 | fn atomic_ref_count(self: Arc<Bar>) {}
| ^^^^ expected struct `Bar`, found struct `std::sync::Arc`
|
= note: expected type `Bar`
= note: found type `std::sync::Arc<Bar>`
This is with Rust 1.15.1.
Before Rust 1.33, there are only four valid method receivers:
struct Foo;
impl Foo {
fn by_val(self: Foo) {} // a.k.a. by_val(self)
fn by_ref(self: &Foo) {} // a.k.a. by_ref(&self)
fn by_mut_ref(self: &mut Foo) {} // a.k.a. by_mut_ref(&mut self)
fn by_box(self: Box<Foo>) {} // no short form
}
fn main() {}
Originally, Rust didn't have this explicit self form, only self, &self, &mut self and ~self (the old name for Box). This changed so that only by-value and by-references have the short-hand built-in syntax, since they are the common cases, and have very key language properties, while all smart pointers (including Box) require the explicit form.
As of Rust 1.33, some additional selected types are available for use as self:
Rc
Arc
Pin
This means that the original example now works:
use std::{rc::Rc, sync::Arc};
struct Bar;
impl Bar {
fn consuming(self) { println!("self") }
fn reference(&self) { println!("&self") }
fn mut_reference(&mut self) { println!("&mut self") }
fn boxed(self: Box<Bar>) { println!("Box") }
fn ref_count(self: Rc<Bar>) { println!("Rc") }
fn atomic_ref_count(self: Arc<Bar>) { println!("Arc") }
}
fn main() {
Bar.consuming();
Bar.reference();
Bar.mut_reference();
Box::new(Bar).boxed();
Rc::new(Bar).ref_count();
Arc::new(Bar).atomic_ref_count();
}
However, the impl handling hasn't yet been fully generalised to match the syntax, so user-created types still don't work. Progress on this is being made under the feature flag arbitrary_self_types and discussion is taking place in the tracking issue 44874.
(Something to look forward to!)
It's now possible to use arbitrary types for self, including Arc<Self>, but the feature is considered unstable and thus requires adding this crate attribute:
#![feature(arbitrary_self_types)]
Using feature crate attributes requires using nightly Rust.

Resources