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

Stream 在多个闭包中表示变量对的生存期,stream,rust,lifetime,borrow-checker,Stream,Rust,Lifetime,Borrow Checker,我正在努力以一种取悦借书人的方式来表达我的代码 我有一个函数create_task,它创建了一些数据库操作的未来。有一个值流,其中每个元素都需要插入到事务中的数据库中。问题是在多个闭包之间共享事务,因为它还可变地借用了连接对象 #![feature(conservative_impl_trait)] extern crate futures; extern crate rusqlite; use futures::prelude::*; use futures::{future, strea

我正在努力以一种取悦借书人的方式来表达我的代码

我有一个函数
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();
        })
}
#![特征(保守的含义特征)]
外部板条箱期货;
外部板条箱rusqlite;
使用期货::前奏::*;
使用未来:{future,stream};
使用rusqlite::Connection;
fn main(){
让任务=创建_任务();
task.wait().unwrap();
}
fn创建_任务()->impl未来{
让mut conn=Connection::open(“temp.db”).unwrap();
conn.execute(“如果不存在创建表temp(val INTEGER)”,&[]).unwrap();
//tx向conn发送一个mut ref!
让tx=conn.transaction().unwrap();
流::iter_ok::(vec![1,2,3])
.对于每个(| val |{
//我在这里借来的!
tx.execute(“插入温度(val)值(?1)”,&[&val]).unwrap();
future::ok(())
})
.地图{
//tx在这里移动/消费!
tx.commit().unwrap();
})
}
该代码存在多个问题:

  • conn
    寿命不够长。它也需要移到关闭处。可能是因为两个闭包而导致的
    Rc
  • 由于易变性要求,
    conn
    不能简单地作为
    Rc
    共享。也许
    Rc
    是一种更合适的类型
  • 借用检查器不知道借用到
    tx
    在每个闭包的第一个
    之后结束,因此无法将其移动到第二个
    映射
    闭包。再一次,将其作为
    Rc
    移动到两个闭包可能是合理的

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

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

你。不要试图这么做

相反,接受对
连接的引用
,并返回包含该生存期的
未来

下一个问题是编译器不知道如何调用闭包,也不知道以什么顺序调用闭包。与其试图结束交易,不如让它从一个“流动”到另一个,让所有权体系确保它始终处于正确的位置

#![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();
        })
}
#![特征(保守的含义特征)]
外部板条箱期货;
外部板条箱rusqlite;
使用期货::前奏::*;
使用未来:{future,stream};
使用rusqlite::Connection;
fn main(){
让mut conn=Connection::open(“temp.db”).unwrap();
conn.execute(“如果不存在创建表temp(val INTEGER)”,&[]).unwrap();
让任务=创建任务(&mut conn);
task.wait().unwrap();
}
fn创建任务impl Future+'a{
让tx=conn.transaction().unwrap();
流::iter_ok::(vec![1,2,3])
.折叠(tx,| tx,val |{
tx.execute(“插入温度(val)值(?1)”,&[&val]).unwrap();
未来::ok(德克萨斯州)
})
.map(移动| tx |{
tx.commit().unwrap();
})
}
一个巨大的警告:如果
execute
不是异步的,你真的不应该在这样的未来使用它。任何阻塞操作都会导致您的所有期货暂停。您可能应该在单独的线程/线程池上运行同步工作负载

另见:


谢谢!明天我会更深入地探究答案。我想指出的一点是,调用方(在本例中是
main
函数)不应该知道存储机制。因此,连接的创建和删除应该由
create\u任务
中的闭包延迟驱动。理想情况下,当流中的第一个值到达时,连接被打开。