表示多个闭包中变量对的生存期



我正在努力以一种取悦借用检查器的方式表达我的代码。

我有一个函数create_task它创造了一些数据库操作的未来。有一个值流,其中每个元素都需要插入到事务中的数据库中。问题是在多个闭包之间共享事务,因为它也可变地借用了连接对象。

#![feature(conservative_impl_trait)]
extern crate futures;
extern crate rusqlite;
use futures::prelude::*;
use futures::{future, stream};
use rusqlite::Connection;
fn main() {
let task = create_task();
task.wait().unwrap();
}
fn create_task() -> impl Future<Item = (), Error = ()> {
let mut conn = Connection::open("temp.db").unwrap();
conn.execute("CREATE TABLE IF NOT EXISTS temp (val INTEGER)", &[]).unwrap();
// tx takes a mut ref to conn!
let tx = conn.transaction().unwrap();
stream::iter_ok::<_, ()>(vec![1, 2, 3])
.for_each(|val| {
// tx borrowed here!
tx.execute("INSERT INTO temp (val) VALUES (?1)", &[&val]).unwrap();
future::ok(())
})
.map(|_| {
// tx moved/consumed here!
tx.commit().unwrap();
})
}

代码存在多个问题:

  • conn活得不够长。它也需要移动到关闭。也许是因为两次关闭而作为Rc<Connection>
  • 由于可变性要求,conn不能简单地作为Rc共享。也许Rc<RefCell<Connection>>更合适的类型?
  • 借款检查器不知道借款到tx在第一个for_each关闭后结束,因此不能移动到第二个map关闭。再一次,将其Rc<Transaction>移动到两个关闭可能是合理的?

我一直在摆弄这些想法,并且知道所需的生命周期是可能的并且有意义,但无法以正确的方式向编译器表达我的代码。

我相信你的第一个问题是你还没有完全理解懒惰的未来有多懒惰。您正在create_task内部创建一个Connection,获取对它的引用,将该引用放入流/未来中,然后尝试返回该未来。此时甚至没有执行任何关闭

不能返回对在函数中创建的值的引用。也不要尝试将事务和连接存储在同一结构中。

相反,接受对Connection的引用并返回包含该生存期的Future

下一个问题是编译器不知道闭包将如何调用或以什么顺序调用。与其试图关闭交易,不如让它从一个"流动"到另一个,让所有权系统确保它始终在正确的位置。

#![feature(conservative_impl_trait)]
extern crate futures;
extern crate rusqlite;
use futures::prelude::*;
use futures::{future, stream};
use rusqlite::Connection;
fn main() {
let mut conn = Connection::open("temp.db").unwrap();
conn.execute("CREATE TABLE IF NOT EXISTS temp (val INTEGER)", &[]).unwrap();
let task = create_task(&mut conn);
task.wait().unwrap();
}
fn create_task<'a>(conn: &'a mut rusqlite::Connection) -> impl Future<Item = (), Error = ()> + 'a {
let tx = conn.transaction().unwrap();
stream::iter_ok::<_, ()>(vec![1, 2, 3])
.fold(tx, |tx, val| {
tx.execute("INSERT INTO temp (val) VALUES (?1)", &[&val]).unwrap();
future::ok(tx)
})
.map(move |tx| {
tx.commit().unwrap();
})
}

一个巨大的警告:如果execute不是异步的,你真的不应该在这样的未来使用它。任何封锁操作都会导致你所有的期货停滞不前。您可能应该在单独的线程/线程池上运行同步工作负载。

另请参阅:

  • 有没有办法返回对函数中创建的变量的引用?
  • 如何在 Rust 中将 SqliteConnection 和 SqliteStatement 对象存储在同一个结构中?
  • 为什么我不能在同一结构中存储值和对该值的引用?

最新更新