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

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.

Related

egui combobox vector for selected

I am trying to use a vector instead of the enum specified in the docs but I have no clue how to implement the selected part. My current code is
egui::ComboBox::from_label("Take your pick")
.selected_text(format!("{}", self.radio[0]))
.show_ui(ui, |ui| {
for i in 0..self.radio.len() {
ui.selectable_value(&mut &self.radio, &self.radio, &self.radio[i]);
}
});
can anyone give me an idea. I do not mind using enum but I do not know how many things will be in it.
I stumbled on the same problem, and I have found a solution. The solution is to add a new Variable to the self. Here is a basic example on the Enum select and the Vector select. Hope this helps out anyone who has run into the same problem.
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use eframe::egui;
fn main() {
let options = eframe::NativeOptions::default();
eframe::run_native(
"Test Select with Enum and Vector",
options,
Box::new(|_cc| Box::new(MyApp::default())),
);
}
#[derive(PartialEq)]
#[derive(Debug)]
enum OS_Select {
First,
Second,
Third,
}
struct MyApp {
selected: usize,
radio: OS_Select,
selector_vec: Vec<String>,
}
impl Default for MyApp {
fn default() -> Self {
Self {
selected: 0,
radio: OS_Select::First,
selector_vec: get_vec(),
}
}
}
fn get_vec() -> Vec<String> {
let vecs = [
"1".to_string(),
"2".to_string(),
"3".to_string(),
].to_vec();
return vecs;
}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Select with enum");
ui.horizontal(|ui| {
ui.radio_value(&mut self.radio, OS_Select::First, "First");
ui.radio_value(&mut self.radio, OS_Select::Second, "Second");
ui.radio_value(&mut self.radio, OS_Select::Third, "Third");
});
ui.end_row();
ui.heading("Select with Vectors");
egui::ComboBox::from_label("Take your pick")
.selected_text(format!("{}", &self.selector_vec[self.selected]))
.show_ui(ui, |ui| {
for i in 0..self.selector_vec.len() {
let value = ui.selectable_value(&mut &self.selector_vec[i], &self.selector_vec[self.selected], &self.selector_vec[i]);
if (value.clicked()) {
self.selected = i;
}
}
});
ui.end_row();
});
}
fn save(&mut self, _storage: &mut dyn eframe::Storage) {}
fn on_close_event(&mut self) -> bool {
true
}
fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) {}
fn auto_save_interval(&self) -> std::time::Duration {
std::time::Duration::from_secs(30)
}
fn max_size_points(&self) -> egui::Vec2 {
egui::Vec2::INFINITY
}
fn clear_color(&self, _visuals: &egui::Visuals) -> egui::Rgba {
// NOTE: a bright gray makes the shadows of the windows look weird.
// We use a bit of transparency so that if the user switches on the
// `transparent()` option they get immediate results.
egui::Color32::from_rgba_unmultiplied(12, 12, 12, 180).into()
// _visuals.window_fill() would also be a natural choice
}
fn persist_native_window(&self) -> bool {
true
}
fn persist_egui_memory(&self) -> bool {
true
}
fn warm_up_enabled(&self) -> bool {
false
}
fn post_rendering(&mut self, _window_size_px: [u32; 2], _frame: &eframe::Frame) {}
}

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

How to get access to enum variant unnamed field?

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

How to create a macro that gets the value of an enum type?

My enum looks like this:
#[derive(Clone, Debug)]
pub enum Type {
GLnull,
GLenum(GLenum),
GLboolean(GLboolean),
GLint(GLint),
GLbyte(GLbyte),
GLshort(GLshort),
GLclampx(GLclampx),
GLubyte(GLubyte),
GLushort(GLushort),
GLuint(GLuint),
GLsizei(GLsizei),
GLclampf(GLclampf),
GLdouble(GLdouble),
GLclampd(GLclampd),
GLfloat_4fv((GLfloat, GLfloat, GLfloat, GLfloat)),
GLfloat(GLfloat),
GLintptr(GLintptr),
GLsizeiptr(GLsizeiptr),
GLbitfield(GLbitfield),
GLchar_ptr(String),
}
macro_rules! get{
($e:expr) => {
match $e {
Type::GLsizei(x) => { x }
Type::GLbitfield(x) => { x }
_ => { 0 }
}
}
}
Now how do I create a macro that gets the value of the enum type?
Like #aochagavia say there is no point to have a macro if you must do specific stuff with your enum.
The following macro could help you, the purpose is to have a macro that create a enum and generate some method. This only work if all variant have one type.
macro_rules! foo {
($($(#[$meta:meta])* foo $name:ident($ty:ty),)*) => {
#[derive(Debug, Clone)]
pub enum Foo {
$($(#[$meta])* $name($ty),)*
}
impl Foo {
pub fn display(&self) {
match *self {
$(Foo::$name(x) => println!("{}", x),)*
}
}
}
}
}
foo! {
foo A(i32),
foo B(i64),
}
fn main() {
let a = Foo::A(32);
let b = Foo::B(64);
a.display();
b.display();
}
The original macro is from #koka-el-kiwi, I take it as an example and modification for your case.
The following method is also available
pub enum Type<T> {
gli32(T),
gli64(T),
glfloat4fv(T),
glString(T),
glVec(T),
}
impl<T> Type<T> {
pub fn unwarp(&self) -> &T {
match *self {
Type::gli32(ref x) => x,
Type::gli64(ref x) => x,
Type::glfloat4fv(ref x) => x,
Type::glString(ref x) => x,
Type::glVec(ref x) => x,
}
}
}
fn main() {
println!("Hello, world!");
let f = Type::gli32(32 as i32);
let ff64 = Type::gli64((64, 32));
let f4fv = Type::glfloat4fv((0.1, 0.2, 0.0));
let cstr = Type::glString(CString::new("glstring").unwrap());
let ve = [1, 2, 3, 5];
let glve = Type::glVec(ve);
println!("f ={} {:?} {:?} {:?}",
f.unwarp(),
f4fv.unwarp(),
cstr.unwarp(),
glve.unwarp());
}

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