【问题标题】:When should we use a struct as opposed to an enum?我们什么时候应该使用结构而不是枚举?
【发布时间】:2018-07-22 05:27:45
【问题描述】:

结构和枚举彼此相似。

什么时候使用结构体比使用枚举更好(反之亦然)?有人可以举一个明确的例子,说明使用结构比使用枚举更可取吗?

【问题讨论】:

  • 另一个小细节:pub enum 公开所有细节(变体及其字段),pub struct 默认情况下仍保持字段私有。如果您需要在公共界面中公开enum 数据但又想隐藏详细信息,则需要将(私有)enum 包装在pub struct 中。

标签: struct enums rust


【解决方案1】:

枚举有多种可能性。结构只有一种可能的“类型”。在数学上,我们说结构是一种产品类型,而枚举是产品的总和。如果您只有一种可能性,请使用结构。例如,空间中的一个点总是将是三个数字。它永远不会是字符串、函数或其他东西。所以它应该是一个包含三个数字的结构。另一方面,如果您正在构建一个数学表达式,它可能是(例如)一个数字或两个由运算符连接的表达式。它有多种可能性,所以它应该是一个枚举。

简而言之,如果结构有效,请使用结构。 Rust 可以围绕它进行优化,任何阅读您的代码的人都会更清楚该值应该被视为什么。

【讨论】:

  • “Rust 可以围绕它进行优化”是什么意思?当您开始使用 codegen 时,结构实际上只是具有单个变体的枚举,因此出于优化目的,它们应该是相同的。
【解决方案2】:

Enum 是具有一组受限值的类型。

enum Rainbow {
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Indigo,
    Violet
}

let color = Red;

match color {
    Red => { handle Red case },
    // all the rest would go here
}

如果需要,您可以将数据存储在 Enum 中。

enum ParseData {
    Whitespace,
    Token(String),
    Number(i32),
}

fn parse(input: String) -> Result<String, ParseData>;

结构是表示事物的一种方式。

struct Window {
    title: String,
    position: Position,
    visible: boolean,
}

现在您可以创建新的Window 对象来代表屏幕上的一个窗口。

【讨论】:

    【解决方案3】:

    也许解释根本区别的最简单方法是枚举包含“变体”,一次只能拥有一个,而结构体包含一个或多个字段,全部你必须拥有的。

    因此,您可以使用enum 来模拟错误代码之类的东西,您一次只能有一个:

    enum ErrorCode {
        NoDataReceived,
        CorruptedData,
        BadResponse,
    }
    

    如果需要,枚举变量可以包含值。例如,我们可以像这样向ErrorCode 添加一个案例:

    enum ErrorCode {
        NoDataReceived,
        CorruptedData,
        BadResponse,
        BadHTTPCode(u16),
    }
    

    在这种情况下,ErrorCode::BadHTTPCode 的实例始终包含 u16

    这使得每个单独的变体表现得有点像tuple struct or unit struct:

    // Unit structs have no fields
    struct UnitStruct;
    
    // Tuple structs contain anonymous values.
    struct TupleStruct(u16, &'static str);
    

    但是,将它们编写为枚举变体的好处是ErrorCode 的每个案例都可以存储在ErrorCode 类型的值中,如下所示(这对于不相关的结构是不可能的)。

    fn handle_error(error: ErrorCode) {
        match error {
            ErrorCode::NoDataReceived => println!("No data received."),
            ErrorCode::CorruptedData => println!("Data corrupted."),
            ErrorCode::BadResponse => println!("Bad response received from server."),
            ErrorCode::BadHTTPCode(code) => println!("Bad HTTP code received: {}", code)
        };
    }
    
    fn main() {
        handle_error(ErrorCode::NoDataReceived); // prints "No data received."
        handle_error(ErrorCode::BadHTTPCode(404)); // prints "Bad HTTP code received: 404"
    }
    

    然后您可以在枚举上match 来确定您获得了哪个变体,并根据它是哪个变体执行不同的操作。


    相比之下,我上面没有提到的第三种结构是最常用的——这是每个人在简单地说“结构”时所指的结构类型。

    struct Response {
        data: Option<Data>,
        response: HTTPResponse,
        error: String,
    }
    
    fn main() {
        let response = Response {
            data: Option::None,
            response: HTTPResponse::BadRequest,
            error: "Bad request".to_owned()
        }
    }
    

    请注意,在这种情况下,为了创建 Response,必须为其所有字段指定值。

    此外,response 的值的创建方式(即HTTPResponse::Something)意味着HTTPResponse 是一个枚举。它可能看起来像这样:

    enum HTTPResponse {
        Ok,         // 200
        BadRequest, // 400
        NotFound,   // 404
    }
    

    【讨论】:

    • 实现特征的结构呢? (即特征 HTTPResponse 和结构 Ok、BadRequest、NotFound)。区别会不会是实现 trait 的结构体有不同的方法实现,但枚举必须有相同的方法实现?
    • @Taztingo 如果我理解您的要求正确,那么答案是肯定的,这是显着差异之一:每个结构的特征可以以不同的方式实现,而您实现特征整个枚举一次。但是,您始终可以在 trait 方法实现中匹配 self,并为每个变体创建有效的不同实现。
    猜你喜欢
    • 2010-09-10
    • 1970-01-01
    • 2011-01-25
    • 2011-01-07
    • 2017-07-16
    • 1970-01-01
    • 1970-01-01
    • 2021-06-13
    相关资源
    最近更新 更多