There is a mutable reference.
If you change transaction
to this:
fn transaction(&mut self) -> Transaction {
let _: () = self;
Transaction{conn: self}
}
You'll see that the compiler errors with:
= note: expected type `()`
= note: found type `&mut Connection`
So self
is of type &mut Connection
... a mutable reference. You're then passing this into the Transaction
instance that is being returned from this function.
That means your mutable borrow exists for the lifetime of trans
(curly braces added by me to show the scope of the borrow):
let mut trans = db_conn.transaction();
{ // <-------------------- Borrow starts here
let mut records_without_sync = 0_usize;
const MAX_RECORDS_WITHOUT_SYNC: usize = 100;
loop {
//do something
records_without_sync += 1;
if records_without_sync >= MAX_RECORDS_WITHOUT_SYNC {
trans.commit();
records_without_sync = 0;
trans = db_conn.transaction();// <--- ####### D'oh! Still mutably borrowed
}
}
} // <-------------------- Borrow ends here
If you're looking for this sort of parent-><-child
setup, I think you'll have to reach for Rc<RefCell>
.
Specifically, an Rc
to reference count how many times you pass the connection around and RefCell
to track borrowing at runtime instead of compile time. Yes, that does mean you'll panic if you manage to try and mutably borrow it twice at runtime. Without knowing more about your architecture its hard to say whether this is suitable or not.
Here is my solution anyway:
use std::cell::RefCell;
use std::rc::Rc;
struct Connection {}
impl Connection {
fn do_something_mutable(&mut self) {
println!("Did something mutable");
}
}
type Conn = Rc<RefCell<Connection>>;
struct Transaction {
conn: Conn,
}
impl Transaction {
fn new(connection: Conn) -> Transaction {
Transaction { conn: connection }
}
fn commit(mut self) {
self.conn.borrow_mut().do_something_mutable();
}
}
fn main() {
let db_conn = Rc::new(RefCell::new(Connection {}));
let mut trans = Transaction::new(db_conn.clone());
let mut records_without_sync = 0_usize;
const MAX_RECORDS_WITHOUT_SYNC: usize = 100;
loop {
//do something
records_without_sync += 1;
if records_without_sync >= MAX_RECORDS_WITHOUT_SYNC {
trans.commit();
records_without_sync = 0;
trans = Transaction::new(db_conn.clone());
break; // Used to stop the loop crashing the playground
}
}
}