借钱不能搬出去

Cannot move out because of borrowing

我有以下 MWE:

use rusqlite::{Connection, Transaction };

fn main() {
    let mut cn: Connection = Connection::open_in_memory().unwrap();
    let tx: Transaction = cn.transaction().unwrap();

    let mut stmt = tx.prepare("
        INSERT INTO mytable (data) VALUES(1)
    ").unwrap();
    stmt.execute([]).unwrap();

    tx.commit().unwrap();
}

在我的IDE(vscode)中,它显示:

tx.prepare显示错误

borrow of tx occurs here

tx.commit();显示错误

cannot move out of tx because it is borrowed

签名:

 pub fn prepare(&self, sql: &str) -> Result<Statement<'_>> {
        self.db.borrow_mut().prepare(self, sql)
 }
 ...
 #[inline]
 pub fn commit(mut self) -> Result<()> {
     self.commit_()
 }

我不明白为什么会显示这些错误。

此外,确定 tx.prepare 的范围可以修复这些错误

use rusqlite::{Connection, Transaction };

fn main() {
    let mut cn: Connection = Connection::open_in_memory().unwrap();
    let tx: Transaction = cn.transaction().unwrap();
    {
        let mut stmt = tx.prepare("
            INSERT INTO mytable (data) VALUES(1)
        ").unwrap();
        stmt.execute([]).unwrap();
    }
    tx.commit().unwrap();
}

谢谢!

Rust Playground

After tx.prepare borrows, what is ownership the Transaction created and assigned to tx ?

不确定我是否理解您的问题。借用不会改变所有权,它是从 所有者那里借用的。所以tx拥有交易对象,stmt借用它,因此所有权不能改变。

Is tx still the owner of the Transaction ?

是的。

When tx.commit() is called, Transaction shoud be owned by tx and be safe to be moved when calling .commit(), no ?

在大多数情况下是的,这里的问题是 Statement 不是一个“普通”类型,它实现了 Drop 这意味着它将(默认情况下)一直存在到它的 drop scope 除非它被显式删除(例如通过调用 drop(stmt))。然后,这将使用 Transaction::commit 进行编译,因为您无法移出“活动借用”。

您可以简单地通过安装与 Rusqlite 相匹配的类型方案来观察此行为:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=32532b41467706d0d80756525a0f43fe

代码可以编译,但是如果您取消注释第 24 行 (impl Drop for …),它会停止编译,并出现与此处相同的错误。

您的解决方案是:

  • 如果 MRE 与实际代码匹配,则无需准备语句,这仅在您有多个查询时才有用
  • 明确地drop()声明一旦你完成它
  • 或者让它存在于自己的块中,因此它的放置范围比函数短

错误消息中给出了此原因:

14 | }
   | - borrow might be used here, when `stmt` is dropped and runs the `Drop` code for type `Statement`

Transaction 上调用 prepare()(通过自动取消引用汇集到 Connection)会得到 Statement<'conn>。换句话说,Statement 借用生命周期为 'conn 的基础 ConnectionConnection 的借用依赖于 Transaction,因为它是 Transaction 的自动解引用给了我们对 Connection.

的借用

每当删除 Statement 时,它都会将 Connection 借用到 clean up behind it。这就是问题所在:您的 stmt 在范围末尾自动删除,在 tx.commit().unwrap() 之后。同时 .commit() 获得了 self 的所有权,因此 Transaction 被移走了。但是 StatementDrop 实现需要 Transaction 的借用是存活的,所以它可以在 Connection 的借用上执行;所以发生错误。

您可以通过简单地引入一个内部范围来解决这个问题,或者在使用它之后手动drop(stmt)