有没有办法在没有模式匹配的情况下直接访问枚举结构中的字段值?

Qui*_*oon 2 enums rust

我希望Rust中的枚举可以像Haskell的生产类型一样使用.我想要

  • 直接访问字段的值
  • 直接指定字段的值或使用更改的值进行克隆.

直接意味着不使用太长的模式匹配代码,但只是可以访问let a_size = a.size.

在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
Run Code Online (Sandbox Code Playgroud)

我尝试了两种样式的Rust枚举定义

风格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);
}
Run Code Online (Sandbox Code Playgroud)

风格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);
}
Run Code Online (Sandbox Code Playgroud)

我无法以任何样式直接访问/分配值.我是否必须实现函数或特征才能访问字段的值?是否有某种方法可以帮助解决这种情况?

Sta*_*eur 5

风格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;
}
Run Code Online (Sandbox Code Playgroud)

如果所有变体都有共同点,为什么要将它们分开呢?


当然,我也需要访问不常见的领域.

这意味着Rust应该定义当运行时的实际类型不包含您需要的字段时要执行的操作.所以,我不认为Rust有一天会添加这个.

你可以自己做.它需要一些代码行,但这与您的Haskell代码的行为相匹配.但是,我不认为这是最好的事情.Haskell是Haskell,我认为你应该在Rust中编写代码而不是尝试使用Rust编写Haskell代码.一般来说,Rust的某些功能直接来自Haskell,但是我想在这里对Rust代码的看法非常奇怪.

#[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);
}
Run Code Online (Sandbox Code Playgroud)

  • @QuietJoon这没有意义,如果你的枚举的运行时类型没有你需要的字段,语言应该怎么做,恐慌? (3认同)

phi*_*mue 4

我认为目前没有size直接访问枚举类型的内置方法。在那之前,enum_dispatch基于宏的解决方案可能会帮助您。

  • 请总结答案中的“enum_dispatch”,以防链接失效。链接不应该是必需的,它应该只是提供或补充答案中提供的信息。 (3认同)