访问本身就是结构字段的枚举变量字段的正确方法
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
中删除 *
。
上面的工作是因为 bool
是 Copy
类型。对于不是 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
的哪些部分实际上是可变借用的。
我有一个结构 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
中删除 *
。
上面的工作是因为 bool
是 Copy
类型。对于不是 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
的哪些部分实际上是可变借用的。