Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

enums

rust

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?

like image 263
QuietJoon Avatar asked Jan 06 '19 12:01

QuietJoon


People also ask

How do I get enum value in Rust?

Bookmark this question. Show activity on this post. struct Point { x: f64, y: f64, } enum Shape { Circle(Point, f64), Rectangle(Point, Point), } let my_shape = Shape::Circle(Point { x: 0.0, y: 0.0 }, 10.0);

Can enums have methods Rust?

In Rust, methods cannot only be defined on structs, they can also be defined on tuples and enums, and even on built-in types like integers.

Can we use enum in struct?

Declaring the enum inside the struct is fine and the way you've laid it out is how I did it as well. You won't be able to access the enum Status outside of the Task struct, but for this challenge that's not something to be concerned about.

What are structs and enums in Rust?

structs can be used to model cars and define state. methods and associated functions can be used to specify their behaviour. enums can be used to specify range of allowed values for a custom data type. traits can be used to describe shared behaviours across user-defined data types.


2 Answers

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);
}
like image 83
Stargateur Avatar answered Oct 23 '22 00:10

Stargateur


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.

like image 35
phimuemue Avatar answered Oct 22 '22 23:10

phimuemue