Rust 为什么对已删除对象的可变引用仍然算作可变引用?

Rust 为什么对已删除对象的可变引用仍然算作可变引用?,rust,lifetime,Rust,Lifetime,以下是一个简化的示例: struct Connection {} impl Connection { fn transaction(&mut self) -> Transaction { Transaction { conn: self } } } struct Transaction<'conn> { conn: &'conn Connection, } impl<'conn> Transaction&

以下是一个简化的示例:

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
        }
    }
}
struct连接{}
impl连接{
fn事务(&mut self)->事务{
事务{conn:self}
}
}
结构事务{
fn提交(多自){}
}
fn main(){
设mut db_conn=Connection{};
让mut trans=db_conn.transaction();//1
让mut记录而不同步=0;
const MAX_RECORDS_而不同步:usize=100;
环路{
//做点什么
不带同步的记录数+=1;
如果记录不同步>=最大记录不同步{
trans.commit();
没有同步的记录=0;
trans=db_conn.transaction();//2
}
}
}
编译器在12报告两个可变借用,但事实并非如此。由于
trans.commit()
按值获取
self
,因此
trans
被删除,因此按点2应该没有可变引用

  • 为什么编译器看不到在2上没有可变引用
  • 我如何修复代码,保留相同的逻辑

  • 有一个可变的引用

    如果将
    事务
    更改为:

    fn transaction(&mut self) -> Transaction {
        let _: () = self;
        Transaction{conn: self}
    }
    
    您将看到编译器出现以下错误:

     = note: expected type `()`
     = note:    found type `&mut Connection`
    
    因此,
    self
    属于
    &mut连接类型
    。。。可变引用。然后将其传递到从该函数返回的
    事务
    实例中

    这意味着您的可变借用在
    trans
    的生命周期内存在(我添加了花括号以显示借用的范围):


    启用以下选项时,原始代码可以正常工作:

    #![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();
            }
        }
    }
    
    #![专题(nll)]
    结构连接{}
    impl连接{
    fn事务(&mut self)->事务{
    事务{conn:self}
    }
    }
    结构事务{
    fn提交(自){}
    }
    fn main(){
    设mut db_conn=Connection{};
    让mut trans=db_conn.transaction();
    让mut记录而不同步=0;
    const MAX_RECORDS_而不同步:usize=100;
    环路{
    //做点什么
    不带同步的记录数+=1;
    如果记录不同步>=最大记录不同步{
    trans.commit();
    没有同步的记录=0;
    trans=db_conn.transaction();
    }
    }
    }
    

    非词汇生存期提高了借用检查器的精度。编译器变得更加智能,现在能够证明更多的程序是内存安全的。

    你不明白我的意思。在
    trans.commit()之后trans不存在。所以不存在可变引用。您的解决方案添加了额外的内存分配,以及额外的计数器。如果没有这些不必要的东西,有可能达到同样的目标吗?谢谢你的回答。@user1244932“所以没有可变的引用”-当然,在运行时。尽管我知道在编译时,借用检查器无法静态地判断这一点。“如果没有这些不必要的东西,有可能实现同样的目标吗?”——我相信是的,但这需要重新构造代码,使其不具有
    父级->@user1244932:有希望,在某个时候,Rust将获得非词汇借用;也就是说,不是通过词法范围(从变量引入到结束
    }
    )跟踪借词,而是根据变量的生存期跟踪借词。不过,我认为现在还没有人在研究它。最终,锈还没有出现。
    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
            }
        }
    }
    
    #![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();
            }
        }
    }