2017-01-19 4 views
3

Вот упрощенный пример:Почему изменчивая ссылка на выпавший объект по-прежнему считается изменчивой ссылкой?

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 
     } 
    } 
} 

Компилятор сообщает о двух изменяемых заимствует на и , но это не так. Поскольку принимает self по значению, trans отбрасывается, поэтому по точкам не должно быть никаких изменяемых ссылок.

  1. Почему компилятор не видит, что в нет изменяемых ссылок?
  2. Как я могу исправить код, оставив ту же логику?
+3

В конце концов, [ваш код просто должен работать] (https://internals.rust-lang.org/t/non-lexical-lifetimes-based-on-liveness/3428) , но Руста еще нет. –

ответ

1

Существует изменчивая ссылка.

Если изменить transaction к этому:

fn transaction(&mut self) -> Transaction { 
    let _:() = self; 
    Transaction{conn: self} 
} 

Вы увидите, что ошибки компилятора с:

= note: expected type `()` 
= note: found type `&mut Connection` 

Так self имеет типа &mut Connection ... изменяемую ссылка. Затем вы передаете это в экземпляр Transaction, который возвращается из этой функции.

Это означает, что ваш изменяемый заем существует в течение всей жизни trans (фигурные скобки добавленные мной, чтобы показать масштабы заема):

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 

Если вы ищете такого рода parent-><-child установки, я подумайте, что вам нужно будет достичь Rc<RefCell>.

В частности, Rc, чтобы указать количество, сколько раз вы проходите соединение и RefCell, чтобы отслеживать заимствования во время выполнения, а не время компиляции. Да, это означает, что вы будете паниковать, если вам удастся попытаться и позаимствовать его дважды во время выполнения. Не зная больше о вашей архитектуре, трудно сказать, подходит ли это или нет.

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 
     } 
    } 
} 
+0

Вы не поняли. После 'trans.commit();' trans не существует. Таким образом, нет изменчивых ссылок. – user1244932

+0

Ваше решение добавляет дополнительное распределение памяти, а также дополнительный счетчик. Можно ли достичь той же цели без таких ненужных вещей? – user1244932

+0

Спасибо за ответ. – user1244932