在所有枚举值中共享一个通用值

25

我有以下代码,其中枚举Message的每个变量都与一个Term值相关联:

type Term = usize;
pub enum Message {
    AppendRequest(Term),
    AppendResponse(Term),
    VoteRequest(Term),
    VoteResponse(Term),
}

impl Message {
    pub fn term(&self) -> Term {
        match *self {
            Message::AppendRequest(term)  => term,
            Message::AppendResponse(term) => term,
            Message::VoteRequest(term) => term,
            Message::VoteResponse(term) =>term,
        }
    }
}

我希望能够在不用分解实际的Message值的情况下获取它的项。我能想到的最好方法是创建一个公共函数来为我解包值,但这感觉很笨拙。如果我添加了新的枚举值,我将不得不记得更新term函数中的匹配语句。

有没有更简洁/人性化的方式来表达上面的代码?有没有一种方法可以说“嘿,这个枚举的每个值都将有一个关联的Term值。”


2
我需要记得在term函数中更新match语句——编译器会提醒你的。 - Shepmaster
1
@Shepmaster 好的。编译器会捕捉到这个错误。或许我本应该说“每次有变更时,我现在需要在两个地方更新代码,感觉好像我在做错了什么”。 - Kurtis Nusbaum
3个回答

36

有没有一种方式可以表达“嘿,这个枚举的每个值都将与一个关联的Term值一起出现”?

没有。通常通过将enum拆分成两部分,并使用包含所有共同部分的struct来处理此问题:

pub struct Message {
    term: Term,
    kind: MessageKind,
}

pub enum MessageKind {
    AppendRequest,
    AppendResponse,
    VoteRequest,
    VoteResponse,
}

2
太好了。这正是我在寻找的。 - Kurtis Nusbaum

15

一个选项是实现 Deref(和/或 DerefMut) trait 来转换为共同部分。

你仍然必须每次添加到枚举时更新该实现,但在使用时减少了样板代码。例如,下面的示例注意到 main 访问枚举中的字段 number

use std::ops::Deref;
use std::string::String;

enum JudgedNumber {
    GoodNumber(Number),
    BadNumber(Number, String),
}

struct Number { number: i32 }

fn main() {
    let nice = JudgedNumber::GoodNumber(Number{number: 42});
    let naughty = JudgedNumber::BadNumber(
        Number{number: 666}, "Damn you to hell".to_string());
    println!("j1 = {}", j1.number);
    println!("j2 = {}", j2.number);
}

impl Deref for JudgedNumber {
    type Target = Number;
    fn deref(&self) -> &Number {
        match self {
            JudgedNumber::GoodNumber(n) => n,
            JudgedNumber::BadNumber(n, _) => n,
        }
    }
}

我从https://github.com/rust-embedded/svd/blob/master/src/svd/cluster.rs中学到了这个。


太酷了!真的很喜欢这个!但是如果常见字段的类型是常见的(比如字符串),而且你不想用只有一个字符串的结构体,我想其他解决方案可能更好。 - undefined

0
DK的回答非常适用于没有任何重要的非常见数据的枚举变体的情况。当你有处理特定变体内部结构的方法时,传递公共字段会变得非常繁琐。
type Term = usize;

struct AppendRequest {
    // Some fields
}

struct AppendResponse {
    // Some fields
}

// other structs

enum MessageKind {
    AppendRequest(AppendRequest),
    AppendResponse(AppendResponse),
    VoteRequest(VoteRequest),
    VoteResponse(VoteResponse),
}

struct Message {
    term: Term,
    kind: MessageKind,
}

fn append(term:Term, request: &AppendRequest) -> (Term, AppendResponse) {
    println!("The term: {}", term);
    todo!()
}

所以,当我第十次着陆在这个页面上时,我写了一个宏库来生成像问题中那样的getter。现在,问题中的代码将如下所示:
use enum_common_fields::EnumCommonFields;

type Term = usize;

struct AppendRequest {
    term: Term,
    // Some fields
}

struct AppendResponse {
    term: Term,
    // Some fields
}

// other structs


#[derive(EnumCommonFields)]
#[common_field(term: String)]
enum Message {
    AppendRequest(AppendRequest),
    AppendResponse(AppendResponse),
    VoteRequest(VoteRequest),
    VoteResponse(VoteResponse),
}

fn append(request: &AppendRequest) -> AppendResponse {
    println!("The term: {}", request.term());
    todo!()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接