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

标签 rust lifetime

这是一个简化的例子:

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() 按值获取 selftrans 被丢弃,所以到点 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在运行时而不是编译时跟踪借用。是的,这确实意味着如果您设法尝试在运行时可变地借用它两次,您会感到 panic 。在不了解您的架构的情况下,很难说这是否合适。

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

关于rust - 为什么对已删除对象的可变引用仍算作可变引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41732404/

相关文章:

rust - 如何指定生命周期以使本地引用值与传入引用不同?

generics - 当其中一个是本地引用时,如何在类型约束中编写引用的生存期?

rust - 为什么需要使用加号运算符 (Iterator<Item = &Foo> + 'a) 为特征添加生命周期?

rust - 有没有办法省略这里特征的生命周期?

rust - 在向量中找到匹配的枚举,并在Rust中返回错误

rust - 找不到类型名为 `par_iter` 的方法

reference - 如何制作引用文献的副本? (生活问题)

sql - 如何对编译时不知道的类型使用 rusqlite 的 Row::get 方法?

networking - 创建PNET数据链接 channel 时出现Rust错误 “PermissionDenied: Operation Not Permitted”

rust - 我应该如何重组图形代码以避免出现 "Cannot borrow variable as mutable more than once at a time"错误?