I'm trying to expose functions associated with a struct so that I can call them from the terminal while the program is running.
I've had some success by defining a trait for this which I implement the on the structs I'm interested in. Although I don't fully grok lifetime annotations, I was able to follow the compiler error messages to add them where appropriate (or at least, to make it work). However, I'm now running into problems with the borrow checker. I understand what the error message is saying, but can't figure out how one would go about restucturing the program to avoid it.
Here's a trimmed down version of the code demonstrating how I'm trying to use it and therefore the problem I'm having:
#[macro_use]
extern crate failure;
use failure::Error;
use std::io;
use std::sync::mpsc;
use std::thread;
use std::collections::HashMap;
pub trait CanCommand {
fn module_commands(&self, commands: String) -> Result<Option<String>, Error>;
}
pub struct CommandInterpreter<'a> {
modules: HashMap<String, &'a dyn CanCommand>,
}
impl<'a> CommandInterpreter<'a> {
pub fn new() -> CommandInterpreter<'a> {
CommandInterpreter {
modules: HashMap::new(),
}
}
pub fn add_module(&mut self, module: String, function_name: &'a CanCommand) {
self.modules.insert(module, function_name);
}
pub fn list_modules(&self) -> Option<Vec<String>> {
match self.modules.is_empty() {
true => None,
false => {
let mut module_list = Vec::new();
for key in self.modules.keys() {
module_list.push(key.to_string());
}
Some(module_list)
}
}
}
pub fn run_command(&self, command: String) -> Result<Option<String>, Error> {
//Splits in two at the first space in the line.
//The first segment needs to match a stored module name.
//The second segment is passed into the associated function.
let mut command = command.splitn(2, ' ');
let module = command.next().unwrap();
match module.is_empty() {
false => {
let module_command = command
.next()
.ok_or_else(|| format_err!("No module command"))?;
match self.modules.get(module) {
Some(&command_function) => {
command_function.module_commands(module_command.to_string())
}
None => Err(format_err!("This is not a module",)),
}
}
true => Err(format_err!("No module")),
}
}
}
struct Window {
title: String,
}
impl Window {
pub fn poll_events(&mut self) -> bool {
true
}
pub fn swap_buffers(&self) -> Result<(), Error> {
Ok(())
}
pub fn get_title(&self) -> String {
self.title.to_owned()
}
}
impl CanCommand for Window {
fn module_commands(&self, commands: String) -> Result<Option<String>, Error> {
//Splits in two at the first colon in the line.
//The first segment, with whitespace trimmed, needs to match a command in this match block.
//The second segment, if it exists, is used for passing in arguments to the called functions.
let mut split_args = commands.splitn(2, ':');
match split_args.next().unwrap().trim() {
"get title" => Ok(Some(
"The window title is ".to_owned() + &self.get_title().to_string(),
)),
_ => Err(format_err!("This is not a valid command.")),
}
}
}
fn main() {
let mut window = Window {
title: "Title".to_string(),
};
println!("Window title is: {}", window.get_title());
let mut command_interpreter = CommandInterpreter::new();
command_interpreter.add_module("window".to_string(), &window);
println!(
"Installed modules: {:?}",
command_interpreter.list_modules().unwrap_or_default()
);
let (command_input, command_received) = mpsc::channel();
let _input_thread = thread::spawn(move || loop {
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
command_input.send(input.trim().to_string()).unwrap();
});
let mut running = true;
while running {
running = window.poll_events();
window.swap_buffers().unwrap();
if let Ok(command) = command_received.try_recv() {
match command_interpreter.run_command(command.to_string()) {
Ok(Some(response)) => println!("{}", response),
Err(message) => println!("{}", message),
_ => (),
};
}
}
}
gives
error[E0502]: cannot borrow `window` as mutable because it is also borrowed as immutable
--> src\main.rs:123:19
|
107 | command_interpreter.add_module("window".to_string(), &window);
| ------- immutable borrow occurs here
...
123 | running = window.poll_events();
| ^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
128 | match command_interpreter.run_command(command.to_string()) {
| ------------------- borrow used here in later iteration of loop
Any pointers would be greatly appreciated!