2017-01-19 58 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 
     } 
    } 
} 

的編譯器報告在和 2個可變借入,但事實並非如此。由於的值爲self,因此trans被刪除,因此按點應該沒有可變引用。

  1. 爲什麼編譯器不能看到在有沒有可變引用?
  2. 如何修復代碼,保留相同的邏輯?
+3

最終,[你的代碼應該只是工作(https://internals.rust-lang.org/t/non-lexical-lifetimes-based-on-liveness/3428) ,但Rust還沒有。 –

回答

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

您的解決方案添加addiotional內存分配,以及其他計數器。沒有這些不必要的事情,是否有可能實現相同的目標? – user1244932

+0

感謝您的回答。 – user1244932

相關問題