Hello,
I'm trying to store references in an array where the values can borrow each other. The borrow checker (rightly) doesn't like this. I want to be able to check this at runtime. The background below hopefully explains why I want this behavior. I'm aware of RefCell
but not sure if that immediately solves the issue.
Background:
I'm creating a small project similar to SQLite for my own learning experience. As part of the sql execution engine there is a virtual machine. the machine is a register based virtual machine and the registers can be primitive values or handles to open tables (cursors). When trying to model this in rust I encounter some interesting problems with the lifetimes, I think the semantics I want are essentially runtime borrow checking ala RefCell but im struggling to work out the details.
Code:
Here is a simplified set of structs for the objects and their lifetimes
struct DataSource {}
struct Database { data_source: DataSource }
struct Cursor<'a> { data_source: &'a DataSource }
struct RowValueRef<'b> { data_source: &'b DataSource}
The virtual machine needs to store the open cursors for one or many tables and also might store some value references too:
enum Value {Literal(i64), Cursor(Cursor<?>), Row(RowValueRef<?>)}
struct VirtualMachine { registers: [Value; 3], db: Database }
and the machine will have many operation functions like:
impl VirtualMachine {
pub fn op_open(&mut self, table: &str, reg: usize) {
self.registers[reg] = Value::Cursor(self.database.open(table));
}
pub fn op_read(&mut self, cursor_reg: usize, reg: usize) {
match self.registers[cursor_reg] {
Value::Cursor(c) => {
let v = c.read_value();
self.registers[reg] = Value::Row(v);
}
_ => panic!()
}
}
}
Problem
The issue with lifetime is that the cursors must outlive the rows.
So when reading from self.registers
I'm not certain if the value it contains is borrowed from another register and if that register still contains the value?
What I want is some type of runtime borrow check. some way the vm can report a lifetime error and abort the virtual program.
- open table
a
intor0
:r0 = open(a)
- read row from
r0
intor1
:r1 = read(r0)
- open table
b
intor0
:r1 = open(a)
- at this point if we read from r1 we should get a runtime error since the cursor that row was read from is now closed. (abort the program)
Obviously any program doing this was badly formed. but currently I struggle to even get something like this past the borrow checker.
How can I achieve this? What changes do I need to make to my existing structs to allow this?