繁体   English   中英

为什么对已删除对象的可变引用仍被视为可变引用?

[英]Why does a mutable reference to a dropped object still count as a mutable reference?

这是一个简化的例子:

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

编译器在12处报告两个可变借位,但事实并非如此。 由于trans.commit()通过值获取self ,因此trans被删除,因此第2点应该没有可变引用。

  1. 为什么编译器在2处没有看到没有可变引用?
  2. 如何修复代码,保留相同的逻辑?

有一个可变的参考。

如果您将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在运行时跟踪借用而不是编译时间。 是的,这确实意味着如果您设法在运行时尝试并可变地借两次,那么您会感到恐慌。 如果不了解您的架构,很难说这是否合适。

无论如何,这是我的解决方案

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();
        }
    }
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM