访问本身就是结构字段的枚举变量字段的正确方法

Right way to access an enum variant's field that is itself a field of a struct

我有一个结构 App:

struct App {
   cmd: Command
}

拥有 Command 类型的命令:

enum Command {
   Cmd1 { flag: bool }
}

(我使用 StructOpt 从中派生命令行界面。)

为了执行正确的命令,我有一个这样的函数:

impl App {
    fn execute(&mut self) {
        match &self.cmd {
            Command::Cmd1 { flag } => self.do_cmd1(*flag)
        };
    }
}

我在额外函数 do_cmd1(&mut self, flag: bool) 中处理实际执行以保持 execute 干净。 但是,这是行不通的,因为在 self.do_cmd1(*flag) 中,我通过 *flag 借用 self 作为可变的和不可变的,它属于 cmd,而 cmd 又属于 self .

我的问题是:在 do_cmd1 中访问 flag 并遵守借用规则的正确方法是什么?

说明:我需要它来处理

enum Command {
    Cmd2 { text: String }
}

变体的字段不是 Copy

这是我想出的解决方案,虽然我认为应该有更好的解决方案:

按以下方式扩展 Command 枚举:

impl Command {
    fn get_cmd1_flag(&self) -> Option<bool> {
        match &self {
            Command::Cmd1 { flag } => Some(*flag),
            _ => None
        }
    }
}

然后,将 do_cmd1 的签名更改为 do_cmd1(&mut self)(即删除 flag 参数)。 要访问 do_cmd1 内部的 flag,您只需调用 self.cmd.get_cmd1_flag() 并处理 Option。 加上适当的 return 类型和 ? 运算符的使用,这甚至写起来很舒服。

我不喜欢这个解决方案的地方是您以某种方式实现了自己的类型检查层,这就是为什么我认为应该有更优雅的方法。但至少这是有效的。

如果您在调用 do_cmd1 之前将标志从 self 中移出或复制出来,则借用不需要重叠。

    fn execute(&mut self) {
        match self.cmd {
            Command::Cmd1 { flag } => self.do_cmd1(flag),
        };
    }

唯一的变化是从 &self.cmd 中删除 & 并从 *flag 中删除 *

上面的工作是因为 boolCopy 类型。对于不是 Copy 的类型,您需要做一些额外的工作以确保借用不会重叠,如以下相关问题:

  • Cannot borrow `*self` as mutable because `self.history[..]` is also borrowed as immutable
  • How can I call a mutating method while holding a reference to self?

do_cmd1真的需要成为App的方法吗?

换句话说:您可以将 cmd 和 "other" 的非 cmd 部分分开(我们称之为 Executor)并将它们放在一起在结构的不同字段中:

struct App {
   cmd: Command
   exe: Executor
}

impl App {
    fn execute(&mut self) {
        match &self.cmd {
            Command::Cmd1 { flag } => self.exe.do_cmd1(*flag)
        };
    }
}

这样一来,self的哪些部分实际上是可变借用的。