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

标签 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

我尝试了两种风格的 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);
}

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

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

最佳答案

风格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;
}

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


Of course, I need to access not common field too.

这意味着 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);
}

关于enums - 有没有办法在没有模式匹配的情况下直接访问枚举结构中的字段值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54061281/

相关文章:

rust - 无法调用类似函数的过程宏: cannot be expanded to statements

rust - 错误 : type mismatch: expected `usize` , 发现 `i64` 与 `Vec<i64>`

rust - 无法调用 rusqlite 的查询,因为它需要类型 &[&rusqlite::types::ToSql]

ios - 在 ios 应用程序中的单例中设置枚举,以便可以在整个应用程序中访问它

python - 将列表理解分配给枚举值

c# - 如何将字符串转换为枚举值再转换为整数?

mysql - 数据模型: Having separate table for a field vs having it as a column in the main table

c++ - 在磁盘上写入时,我应该使用尽可能小的类型吗?

arrays - 线程 '<main>' 在创建大型数组时已溢出其堆栈

module - `alloc::rc::Rc` 和 `std::rc::Rc` 有什么区别?