QuietJoon
QuietJoon

Reputation: 491

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

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?

Upvotes: 6

Views: 3187

Answers (2)

Stargateur
Stargateur

Reputation: 26697

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);
}

Upvotes: 7

phimuemue
phimuemue

Reputation: 35973

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.

Upvotes: 4

Related Questions