I have the following code:
struct Variable {
value: i32,
}
struct Statement<'a> {
variables: Vec<&'a Variable>,
}
impl Statement<'_> {
fn to_string(&self) -> String {
let mut result = String::new();
for variable in &self.variables {
result += &variable.value.to_string();
result += ", ";
}
result
}
}
struct Module<'a> {
variables: Vec<Variable>,
statements: Vec<Statement<'a>>,
}
impl<'a> Module<'a> {
fn add_statement(&mut self, statement: Statement<'a>) -> &Statement {
self.statements.push(statement);
self.statements.last().unwrap()
}
fn add_variable(&mut self, variable: Variable) -> &Variable {
self.variables.push(variable);
self.variables.last().unwrap()
}
}
fn main() {
let mut module = Module {
variables: Vec::new(),
statements: Vec::new(),
};
let var0 = module.add_variable(Variable { value: 10 });
println!("Init var0: {}", var0.value);
let var1 = module.add_variable(Variable { value: 11 });
println!("Init var1: {}", var1.value);
let stmt0 = module.add_statement(Statement {
variables: Vec::new(),
});
stmt0.variables.push(var0);
}
There are errors in main
, as I believe var0
and var1
both borrow module
as mutable. So when I try to mutate module
, conflicts arise. I'm wondering what's the best solution to this.
Ideally, I want module
to own all the variables and statements; and statements only have a reference to the variables it needs (e.g. for self-stringification). Therefore, the lifetime of statements and variables should be less than the module that owns it. I do see a problem with the possibility of a module's variable being removed and therefore there being an invalid reference to a variable, but I'm unsure about the best way to address this issue as well.
Below is more or less my end goal, but with extreme usage of Rc
and RefCell
, avoiding much of Rust's ownership principles.
use std::cell::RefCell;
use std::rc::Rc;
struct Variable {
value: i32,
}
struct Statement {
variables: Vec<Rc<RefCell<Variable>>>,
}
impl Statement {
fn to_string(&self) -> String {
let mut result = String::new();
for variable in &self.variables {
result += &variable.borrow().value.to_string();
result += ", ";
}
result
}
}
struct Module {
variables: Vec<Rc<RefCell<Variable>>>,
statements: Vec<Rc<RefCell<Statement>>>,
}
impl Module {
fn add_statement(&mut self, statement: Statement) -> Rc<RefCell<Statement>> {
let rc_statement = Rc::new(RefCell::new(statement));
self.statements.push(rc_statement.clone());
rc_statement
}
fn add_variable(&mut self, variable: Variable) -> Rc<RefCell<Variable>> {
let rc_variable = Rc::new(RefCell::new(variable));
self.variables.push(rc_variable.clone());
rc_variable
}
}
fn main() {
let mut module = Module {
variables: Vec::new(),
statements: Vec::new(),
};
let var0 = module.add_variable(Variable { value: 10 });
println!("Init var0: {}", var0.borrow().value);
let var1 = module.add_variable(Variable { value: 11 });
println!("Init var1: {}", var1.borrow().value);
let stmt0 = module.add_statement(Statement {
variables: Vec::new(),
});
stmt0.borrow_mut().variables.push(var0.clone());
stmt0.borrow_mut().variables.push(var1.clone());
println!("Init stmt0: {}", stmt0.borrow().to_string());
let var2 = module.add_variable(Variable { value: 12 });
let stmt1 = module.add_statement(Statement {
variables: Vec::new(),
});
println!("Init stmt1: {}", stmt1.borrow().to_string());
println!("Init var1: {}", var1.borrow().value);
stmt1.borrow_mut().variables.push(var1.clone());
println!("After 1 stmt1: {}", stmt1.borrow().to_string());
stmt1.borrow_mut().variables.push(var2.clone());
println!("End stmt0: {}", stmt0.borrow().to_string());
println!("End stmt1: {}", stmt1.borrow().to_string());
}