3

Here is a simplified example:

struct Connection {}

impl Connection {
    fn transaction(&mut self) -> Transaction {
        Transaction { conn: self }
    }
}

struct Transaction<'conn> {
    conn: &'conn Connection,
}

impl<'conn> Transaction<'conn> {
    fn commit(mut self) {}
}

fn main() {
    let mut db_conn = Connection {};

    let mut trans = db_conn.transaction(); //1
    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(); //2
        }
    }
}

The compiler reports two mutable borrows at 1 and 2, but that is not the case. Since trans.commit() takes self by value, trans is dropped, so by point 2 there should be no mutable references.

  1. Why can the compiler not see that at 2 there are no mutable references?
  2. How can I fix the code, leaving the same logic?
ljedrz
  • 20,316
  • 4
  • 69
  • 97
user1244932
  • 7,352
  • 5
  • 46
  • 103
  • 3
    Eventually, [your code should just work](https://internals.rust-lang.org/t/non-lexical-lifetimes-based-on-liveness/3428), but Rust is not there yet. – Francis Gagné Jan 19 '17 at 03:42

2 Answers2

2

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
        }
    }
}
Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
  • You not get my point. After `trans.commit();` trans not exists. So there is no mutable references. – user1244932 Jan 19 '17 at 03:25
  • Your solution add addiotional memory allocation, plus additional counter. Is it possible to achieve the same goal without such unnecessary things? – user1244932 Jan 19 '17 at 03:27
  • Thanks for answer. – user1244932 Jan 19 '17 at 03:27
  • @user1244932 "So there is no mutable references" - sure, at runtime. The borrow checker can't tell that statically though at compile time that I am aware of. "Is it possible to achieve the same goal without such unnecessary things?" - I believe so, but it would require restructuring your code to not have the `parent-><-child` relationship held together via references. When I get more time tonight I could show you what I mean in that regard. – Simon Whitehead Jan 19 '17 at 03:37
  • 1
    @user1244932: There is hope that, at some point, Rust will get non-lexical borrowing; that is, that instead of borrows being tracked by lexical scope (from variable introduction to closing `}`) they will instead be tracked based on the lifetime of the variable. I don't think anyone is working on it right now, though. – Matthieu M. Jan 19 '17 at 12:38
2

Your original code works when non-lexical lifetimes are enabled:

#![feature(nll)]

struct Connection {}

impl Connection {
    fn transaction(&mut self) -> Transaction {
        Transaction { conn: self }
    }
}

struct Transaction<'conn> {
    conn: &'conn Connection,
}

impl<'conn> Transaction<'conn> {
    fn commit(self) {}
}

fn main() {
    let mut db_conn = Connection {};

    let mut trans = db_conn.transaction();
    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();
        }
    }
}

Non-lexical lifetimes improve the precision of the borrow checker. The compiler has become smarter and it is now able to prove that more programs are memory safe.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366