How to get access to enum variant unnamed field? - enums

I would like to print data of tuple enum without named fields.
A tuple is a general way of grouping together some number of other values with a variety of types into one compound type.
#[derive(Debug)]
enum Coin {
Penny(String),
Nickel { id: String },
}
fn main() {
let penny = Coin::Penny(String::from("penny"));
let nickel: Coin = Coin::Nickel { id: String::from("nickel") };
println!("{} {:?} ", penny.0, penny);
println!("{:?}", nickel);
}
In this example, Nickel is a struct-like enum variant, whereas Penny is simply called an enum variant.
I get a compiler error:
error[E0609]: no field `0` on type `Coin`
For more information about this error, try `rustc --explain E0609`.

You can use a match statement or alternatively an if let statement to use the variant's associated value:
#[derive(Debug)]
enum Coin {
Penny(String),
Nickel { id: String },
}
fn main() {
let penny = Coin::Penny(String::from("penny"));
let nickel: Coin = Coin::Nickel {
id: String::from("nickel"),
};
if let Coin::Penny(name) = penny {
println!("{}", name);
}
if let Coin::Nickel{ id } = nickel {
println!("{}", id);
}
}
Playground link

Example using match statement:
#[derive(Debug)]
enum Coin {
Penny(String),
Nickel { id: String },
}
fn main() {
let penny = Coin::Penny(String::from("penny"));
let nickel: Coin = Coin::Nickel {
id: String::from("nickel"),
};
match &penny {
Coin::Penny(id) => {
println!("{}; {:?}", id, penny);
}
_ => {}
}
match &nickel {
Coin::Nickel { id } => {
println!("{}; {:?}", id, nickel);
}
_ => {}
}
}
Playground link

Related

Compare nested enum variants in Rust

Learning Rust I happen to need to compare variants inside nested enums. Considering following enums how do I compare actual variants of instantinated BuffTurget?
enum Attribute {
Strength,
Agility,
Intellect,
}
enum Parameter {
Health,
Mana,
}
enum BuffTarget {
Attribute(Attribute),
Parameter(Parameter),
}
Searching the web led me to the `discriminant and particularly to comparison function like this:
fn variant_eq<T>(a: &T, b: &T) -> bool {
std::mem::discriminant(a) == std::mem::discriminant(b)
}
Unfortunately this function is not working in my case:
#[test]
fn variant_check_is_working() {
let str = BuffTarget::Attribute(Attribute::Strength);
let int = BuffTarget::Attribute(Attribute::Intellect);
assert_eq!(variant_eq(&str, &int), false);
}
// Output:
// thread 'tests::variant_check' panicked at 'assertion failed: `(left == right)`
// left: `true`,
// right: `false`', src/lib.rs:11:9
Ideally I would like my code to be something like this, using if let:
let type_1 = get_variant(BuffTarget::Attribute(Attribute::Strength));
let type_2 = get_variant(BuffTarget::Attribute(Attribute::Intellect));
if let type_1 = type_2 {
println!("Damn it!")
} else { println!("Success!!!") }
Found suitable solution in this answer. Use #[derive(PartialEq)] for the enums to compare them with ==: enum_1 == enum_2.
#[derive(PartialEq)]
enum Attribute {
Strength,
Agility,
Intellect,
}
#[derive(PartialEq)]
enum Parameter {
Health,
Mana,
}
#[derive(PartialEq)]
enum BuffTarget {
Attribute(Attribute),
Parameter(Parameter),
}
let type_1 = BuffTarget::Attribute(Attribute::Strength));
let type_2 = BuffTarget::Attribute(Attribute::Intellect));
assert_eq!((type_1 == type_2), false);

How to read a value of an enum which associates with a custom type in Rust?

I have an implementation in Rust as follows. In the main function, I am reading a value in SalaryRange enum and this will display High("So High").
// This can be a complex type, just using string for the question
type SRange = String;
type SalEnu = SalaryRange<SRange>;
struct User<SRange> {
username: String,
email: String,
sign_in_count: u64,
active: bool,
income: Income<SRange>,
}
struct Income<SRange> {
salary_range: SalaryRange<SRange>
}
#[derive(Debug)]
enum SalaryRange<SRange> {
Low(SRange),
Mid(SRange),
High(SRange),
}
fn main() {
let user1 = User {
email: String::from("test#email.com"),
username: String::from("test_name"),
active: true,
sign_in_count: 1,
income: Income {
salary_range: (
SalaryRange::High("So High")
)
},
};
let mut srange: SalaryRange<&str> = user1.income.salary_range;
println!("{:?}", srange);
}
Link for this example can be found here.
Just wanted to know if there is a possibility to read and print the value in that enum as println!("{:?}", srange::High);, just to print out the string value?
I only want to print the value So High.
If I use srange::High This will throw an error saying
println!("{:?}", srange::High);
| ^^^^^^ use of undeclared type or module `srange`
error: aborting due to previous error
You can implement a method on your enum to extract the value:
#[derive(Debug)]
enum SalaryRange<S> {
Low(S),
Mid(S),
High(S),
}
impl<S> SalaryRange<S> {
fn value(&self) -> &S {
match self {
SalaryRange::Low(value) => value,
SalaryRange::Mid(value) => value,
SalaryRange::High(value) => value,
}
}
}
println!("{:?}", srange.value());
You can pattern match srange with the if let syntax.
if let SalaryRange::High(s) = srange {
println!("{}", s);
}
will print "so high".
I know it's been a while since the question has been opened, but I would like to complete Peter's answer.
There is a more idiomatic way to achieve what you want. Just implement the std::fmt::Display trait to your enum as following:
pub enum SalaryRange {
LOW(String),
MID(String),
HIGH(String),
}
impl std::fmt::Display for SalaryRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let content = match self {
SalaryRange::LOW(content) => content,
SalaryRange::MID(content) => content,
SalaryRange::HIGH(content) => content,
};
write!(f, "{}", content)
}
}
The std::fmt::Display trait allows you to display the content held by your enum value like this:
let salary_range = SalaryRange::HIGH("So high".to_string());
println!("{}", salary_range);
// outputs: "So high"
This should work with any type.
Playground to test it: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=eaea33a955dc9dcd81a4b96ec22d82bd

Is there a way to directly access a field value in an enum struct without pattern matching?

I wish that enums in Rust can be used like Haskell's productive type. I want to
access a field's value directly
assign a field's value directly or make a clone with the changing value.
Directly means that not using too long pattern matching code, but just could access like let a_size = a.size.
In Haskell:
data TypeAB = A {size::Int, name::String} | B {size::Int, switch::Bool} deriving Show
main = do
let a = A 1 "abc"
let b = B 1 True
print (size a) -- could access a field's value directly
print (name a) -- could access a field's value directly
print (switch b) -- could access a field's value directly
let aa = a{size=2} -- could make a clone directly with the changing value
print aa
I tried two styles of Rust enum definition like
Style A:
#[derive(Debug)]
enum EntryType {
A(TypeA),
B(TypeB),
}
#[derive(Debug)]
struct TypeA {
size: u32,
name: String,
}
#[derive(Debug)]
struct TypeB {
size: u32,
switch: bool,
}
fn main() {
let mut ta = TypeA {
size: 3,
name: "TAB".to_string(),
};
println!("{:?}", &ta);
ta.size = 2;
ta.name = "TCD".to_string();
println!("{:?}", &ta);
let mut ea = EntryType::A(TypeA {
size: 1,
name: "abc".to_string(),
});
let mut eb = EntryType::B(TypeB {
size: 1,
switch: true,
});
let vec_ab = vec![&ea, &eb];
println!("{:?}", &ea);
println!("{:?}", &eb);
println!("{:?}", &vec_ab);
// Want to do like `ta.size = 2` for ea
// Want to do like `ta.name = "bcd".to_string()` for ea
// Want to do like `tb.switch = false` for eb
// ????
println!("{:?}", &ea);
println!("{:?}", &eb);
println!("{:?}", &vec_ab);
}
Style B:
#[derive(Debug)]
enum TypeCD {
TypeC { size: u32, name: String },
TypeD { size: u32, switch: bool },
}
fn main() {
// NOTE: Rust requires representative struct name before each constructor
// TODO: Check constructor name can be duplicated
let mut c = TypeCD::TypeC {
size: 1,
name: "abc".to_string(),
};
let mut d = TypeCD::TypeD {
size: 1,
switch: true,
};
let vec_cd = vec![&c, &d];
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
// Can't access a field's value like
// let c_size = c.size
let c_size = c.size; // [ERROR]: No field `size` on `TypeCD`
let c_name = c.name; // [ERROR]: No field `name` on `TypeCD`
let d_switch = d.switch; // [ERROR]: No field `switch` on `TypeCD`
// Can't change a field's value like
// c.size = 2;
// c.name = "cde".to_string();
// d.switch = false;
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
}
I couldn't access/assign values directly in any style. Do I have to implement functions or a trait just to access a field's value? Is there some way of deriving things to help this situation?
What about style C:
#[derive(Debug)]
enum Color {
Green { name: String },
Blue { switch: bool },
}
#[derive(Debug)]
struct Something {
size: u32,
color: Color,
}
fn main() {
let c = Something {
size: 1,
color: Color::Green {
name: "green".to_string(),
},
};
let d = Something {
size: 2,
color: Color::Blue { switch: true },
};
let vec_cd = vec![&c, &d];
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
let _ = c.size;
}
If all variant have something in common, why separate them?
Of course, I need to access not common field too.
This would imply that Rust should define what to do when the actual type at runtime doesn't contain the field you required. So, I don't think Rust would add this one day.
You could do it yourself. It will require some lines of code, but that matches the behavior of your Haskell code. However, I don't think this is the best thing to do. Haskell is Haskell, I think you should code in Rust and not try to code Haskell by using Rust. That a general rule, some feature of Rust come directly from Haskell, but what you want here is very odd in my opinion for Rust code.
#[derive(Debug)]
enum Something {
A { size: u32, name: String },
B { size: u32, switch: bool },
}
impl Something {
fn size(&self) -> u32 {
match self {
Something::A { size, .. } => *size,
Something::B { size, .. } => *size,
}
}
fn name(&self) -> &String {
match self {
Something::A { name, .. } => name,
Something::B { .. } => panic!("Something::B doesn't have name field"),
}
}
fn switch(&self) -> bool {
match self {
Something::A { .. } => panic!("Something::A doesn't have switch field"),
Something::B { switch, .. } => *switch,
}
}
fn new_size(&self, size: u32) -> Something {
match self {
Something::A { name, .. } => Something::A {
size,
name: name.clone(),
},
Something::B { switch, .. } => Something::B {
size,
switch: *switch,
},
}
}
// etc...
}
fn main() {
let a = Something::A {
size: 1,
name: "Rust is not haskell".to_string(),
};
println!("{:?}", a.size());
println!("{:?}", a.name());
let b = Something::B {
size: 1,
switch: true,
};
println!("{:?}", b.switch());
let aa = a.new_size(2);
println!("{:?}", aa);
}
I think there is currently no built-in way of accessing size directly on the enum type. Until then, enum_dispatch or a macro-based solution may help you.

How do I conditionally check if an enum is one variant or another?

I have an enum with two variants:
enum DatabaseType {
Memory,
RocksDB,
}
What do I need in order to make a conditional if inside a function that checks if an argument is DatabaseType::Memory or DatabaseType::RocksDB?
fn initialize(datastore: DatabaseType) -> Result<V, E> {
if /* Memory */ {
//..........
} else if /* RocksDB */ {
//..........
}
}
First have a look at the free, official Rust book The Rust Programming Language, specifically the chapter on enums.
match
fn initialize(datastore: DatabaseType) {
match datastore {
DatabaseType::Memory => {
// ...
}
DatabaseType::RocksDB => {
// ...
}
}
}
if let
fn initialize(datastore: DatabaseType) {
if let DatabaseType::Memory = datastore {
// ...
} else {
// ...
}
}
==
#[derive(PartialEq)]
enum DatabaseType {
Memory,
RocksDB,
}
fn initialize(datastore: DatabaseType) {
if DatabaseType::Memory == datastore {
// ...
} else {
// ...
}
}
matches!
This is available since Rust 1.42.0
fn initialize(datastore: DatabaseType) {
if matches!(datastore, DatabaseType::Memory) {
// ...
} else {
// ...
}
}
See also:
How to compare enum without pattern matching
Read from an enum without pattern matching
Compare enums only by variant, not value
https://doc.rust-lang.org/std/macro.matches.html
// A simple example that runs in rust 1.58:
enum Aap {
Noot(i32, char),
Mies(String, f64),
}
fn main() {
let aap: Aap = Aap::Noot(42, 'q');
let noot: Aap = Aap::Mies(String::from("noot"), 422.0);
println!("{}", doe(aap));
println!("{}", doe(noot));
}
fn doe(a: Aap) -> i32 {
match a {
Aap::Noot(i, _) => i,
Aap::Mies(_, f) => f as i32,
}
}

Modifying a value inside an enum while matching

Is it possible to directly modify a value embedded inside an enum?
The following fails with error: cannot borrow immutable anonymous field `a.0` as mutable, even though I used ref mut.
enum Foo {
Bar(usize),
}
fn main() {
let a = Foo::Bar(10);
match a {
Foo::Bar(ref mut val) => *val = 33,
}
match a {
Foo::Bar(val) => println!("{}", val), // should print 33
}
}
That's not a huge problem because I can do the following as a work-around:
match a {
Foo::Bar(val) => a = Foo::Bar(33),
}
But is this the correct way?
You need to make the binding to a mutable.
enum Foo {
Bar(usize),
}
fn main() {
let mut a = Foo::Bar(10);
match a {
Foo::Bar(ref mut val) => *val = 33,
}
match a {
Foo::Bar(val) => println!("{}", val), // 33
}
}

Resources