我试图在Rust中实现命令行服务器应用程序。 我希望有一个命令列表(或哈希表),我可以迭代这些命令来打印用法和查找/执行命令。
我的问题是每个Command
都需要对命令用来执行的事物的可变引用(例如将用户插入数据库或其他东西)。借用检查员显然不喜欢传递多个可变引用。有没有办法做到这一点?我首先让execute
方法引用它所需要的东西,然后我需要一个单独的列表来查找需要不同的东西的命令,听起来它会变得笨拙。
以下是问题的一个示例:
struct SomeStruct {
pub some_field: String,
}
impl SomeStruct {
pub fn new(field: String) -> SomeStruct {
let some_struct = SomeStruct {
some_field: field,
};
return some_struct;
}
pub fn change_field(&mut self) {
self.some_field = "Something else".to_string();
}
}
struct SomeCommand<'a> {
pub some_struct: &'a mut SomeStruct,
}
impl<'a> SomeCommand<'a> {
pub fn new(the_struct: &'a mut SomeStruct) -> SomeCommand {
let some_command = SomeCommand {
some_struct: the_struct,
};
return some_command;
}
pub fn execute(&mut self) {
self.some_struct.change_field();
}
}
fn main() {
let mut some_struct = SomeStruct::new("hey".to_string());
let some_command1 = SomeCommand::new(&mut some_struct);
// Compiler complains because I'm making another mutable binding to some_struct
let some_command2 = SomeCommand::new(&mut some_struct);
}
有更好的方法吗?
答案 0 :(得分:2)
我认为将可变引用作为execute()
的参数传递而不是将其存储在SomeCommand
中是可行的方法。不要让参考文献的生存时间超过他们的生活时间。
但这是一个相当广泛的问题:我可以想到十几个可能的解决方案 - 很难说出你的情况最好,因为你的例子非常通用。也许在你告诉我们更多信息之后我们可以更具体一些(可能是你要实现的一小部分功能)。
只看你帖子的标题:许多Rust项目使用docopt
- AFAIK甚至cargo
使用它。但是,我怀疑它对您的主要设计问题没有帮助。
此外:在new
方法中,由于隐式返回,您可以删除一些代码。这样:
pub fn new(field: String) -> SomeStruct {
let some_struct = SomeStruct {
some_field: field,
};
return some_struct;
}
......变成了这个:
pub fn new(field: String) -> SomeStruct {
SomeStruct {
some_field: field,
}
}
答案 1 :(得分:2)
我试图在 Rust 中做同样的事情,遵循设计模式一书中的例子。这里的问题是我们需要维护 Command
trait 的通用接口,这意味着我们不应该将 trait 设计为采用某些类型的特定对象。我们剩下的唯一解决方案是在实现 Command
特征的对象的具体实例中存储对对象本身的可变引用。但是,如果我们使用 &'a mut ...
,Rust 编译器将不喜欢对单个对象的多个可变引用,这意味着对于我们想要执行命令的任何给定对象,我们只能有一个 Command
实例,并且我们只能调用一次执行。
这可以通过使用 RefCell
或 Arc<Mutex<>>
来实现。我已经实现了它们,它们都运行良好。不同的是,RefCell
不是线程安全的,所以如果你选择以这种方式实现它,你就不能跨线程共享同一个 Command
对象;而 Arc<Mutex<>>
是线程安全的。
以下是我的实现:
trait Command {
fn execute(&mut self); // Execute command.
fn is_reversible(&self) -> bool; // Undoable operation?
fn unexecute(&mut self); // Undo command.
}
// ------ Using RefCell ------
struct ChangeFontSizeCommand<'a> {
text: &'a RefCell<dyn Text>,
old_size: Option<usize>,
new_size: usize
}
impl<'a> Command for ChangeFontSizeCommand<'a> {
// Implementation... (many calls to .borrow() and .borrow_mut())
}
impl<'a> ChangeFontSizeCommand<'a> {
pub fn new(text: &'a RefCell<dyn Text>, new_size: usize) -> Self {
// Implementation...
}
}
// ------ Using Arc and Mutex ------
struct ChangeFontColorCommand {
text: Arc<Mutex<dyn Text>>,
old_color: Option<Color>,
new_color: Color
}
impl Command for ChangeFontColorCommand {
// Implementation... (many calls to .lock().unwrap())
}
impl ChangeFontColorCommand {
pub fn new(text: Arc<Mutex<dyn Text>>, new_color: Color) -> Self {
// Implementation...
}
}
请注意,在两个示例中,RefCell
或 Arc<Mutex<>>
的实例必须在对象初始值设定项的外部创建,我们不能传入可变引用并在其内部创建它们命令实现结构,这将违反 Rust 的借用检查器规则。