gpt4 book ai didi

rust - rusqlite MappedRows 的返回类型

转载 作者:行者123 更新时间:2023-11-29 08:22:25 31 4
gpt4 key购买 nike

我正在尝试编写一个返回 rusqlite::MappedRows 的方法:

pub fn dump<F>(&self) -> MappedRows<F>
where F: FnMut(&Row) -> DateTime<UTC>
{
let mut stmt =
self.conn.prepare("SELECT created_at FROM work ORDER BY created_at ASC").unwrap();

let c: F = |row: &Row| {
let created_at: DateTime<UTC> = row.get(0);
created_at
};

stmt.query_map(&[], c).unwrap()
}

我遇到了一个编译器错误:

error[E0308]: mismatched types
--> src/main.rs:70:20
|
70 | let c: F = |row: &Row| {
| ____________________^ starting here...
71 | | let created_at: DateTime<UTC> = row.get(0);
72 | | created_at
73 | | };
| |_________^ ...ending here: expected type parameter, found closure
|
= note: expected type `F`
= note: found type `[closure@src/main.rs:70:20: 73:10]`

我在这里做错了什么?

我尝试将闭包直接传递给 query_map 但我得到了相同的编译器错误。

最佳答案

我将把答案分成两部分,第一部分是关于如何在不考虑借用检查器的情况下修复返回类型,第二部分是关于为什么即使你修复了返回类型它也不起作用。


§1.

每个闭包都有一个唯一的匿名类型,所以 c不能是任何类型 F 来电者提供。这意味着这一行永远不会编译:

let c: F = |row: &Row| { ... } // no, wrong, always.

相反,类型应该从 dump 传播出去功能,即类似于:

//         ↓ no generics
pub fn dump(&self) -> MappedRows<“type of that c”> {
..
}

Stable Rust 不提供命名该类型的方法。但是我们可以在夜间使用“impl Trait”功能这样做:

#![feature(conservative_impl_trait)]

// ↓~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pub fn dump(&self) -> MappedRows<impl FnMut(&Row) -> DateTime<UTC>> {
..
}
// note: wrong, see §2.

impl F这里的意思是,“我们将返回一个 MappedRows<T>输入 T: F , 但我们不会具体说明 T 到底是什么;调用者应该准备好处理任何满足 F 的事情作为T的候选人”。

由于您的闭包不捕获任何变量,您实际上可以转 c成一个函数。我们可以命名一个函数指针类型,而不需要“impl Trait”。

//                               ↓~~~~~~~~~~~~~~~~~~~~~~~~
pub fn dump(&self) -> MappedRows<fn(&Row) -> DateTime<UTC>> {
let mut stmt = self.conn.prepare("SELECT created_at FROM work ORDER BY created_at ASC").unwrap();

fn c(row: &Row) -> DateTime<UTC> {
row.get(0)
}

stmt.query_map(&[], c as fn(&Row) -> DateTime<UTC>).unwrap()
}
// note: wrong, see §2.

无论如何,如果我们使用“impl Trait”,因为MappedRows用作Iterator,直接这么说比较合适:

#![feature(conservative_impl_trait)]

pub fn dump<'c>(&'c self) -> impl Iterator<Item = Result<DateTime<UTC>>> + 'c {
..
}
// note: wrong, see §2.

(如果没有 'c 边界,编译器会报错 E0564,似乎生命周期省略不适用于 impl Trait)

如果您坚持使用 Stable Rust,则无法使用“impl Trait”功能。您可以将 trait 对象包装在一个 Box 中,以堆分配和动态调度为代价:

pub fn dump(&self) -> Box<Iterator<Item = Result<DateTime<UTC>>>> {
...
Box::new(stmt.query_map(&[], c).unwrap())
}
// note: wrong, see §2.

§2.

如果你想,上面的修复是有效的,比方说,只是return an independent closure or iterator .但是如果你返回 rusqlite::MappedRows 它就不起作用了.由于 lifetime 问题,编译器将不允许上述工作:

error: `stmt` does not live long enough
--> 1.rs:23:9
|
23 | stmt.query_map(&[], c).unwrap()
| ^^^^ does not live long enough
24 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:80...
--> 1.rs:15:81
|
15 | pub fn dump(conn: &Connection) -> MappedRows<impl FnMut(&Row) -> DateTime<UTC>> {
| ^

这是正确的。 MappedRows<F> 实际上是 MappedRows<'stmt, F> ,此类型仅在原始 SQLite 语句对象(具有 'stmt 生命周期)超过它时才有效——因此编译器会提示 stmt返回函数时已死。

确实,如果在我们迭代这些行之前删除该语句,我们将得到垃圾结果。不好!<​​/p>

我们需要做的是确保在删除语句之前读取所有行

您可以将行收集到一个向量中,从而解除结果与语句的关联,代价是将所有内容都存储在内存中:

//                    ↓~~~~~~~~~~~~~~~~~~~~~~~~~
pub fn dump(&self) -> Vec<Result<DateTime<UTC>>> {
..
let it = stmt.query_map(&[], c).unwrap();
it.collect()
}

或者反转控制,让dump接受一个函数,dump会在保持stmt的同时打电话活着,代价是使调用语法变得怪异:

//                    ↓~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pub fn dump<F>(&self, mut f: F) where F: FnMut(Result<DateTime<UTC>>) {
...
for res in stmt.query_map(&[], c).unwrap() {
f(res);
}
}

x.dump(|res| println!("{:?}", res));

或者拆分dump分成两个函数,并让调用者保持语句事件,代价是向用户公开一个中间结构:

#![feature(conservative_impl_trait)]

pub fn create_dump_statement(&self) -> Statement {
self.conn.prepare("SELECT '2017-03-01 12:34:56'").unwrap()
}

pub fn dump<'s>(&self, stmt: &'s mut Statement) -> impl Iterator<Item = Result<DateTime<UTC>>> + 's {
stmt.query_map(&[], |row| row.get(0)).unwrap()
}

...

let mut stmt = x.create_dump_statement();
for res in x.dump(&mut stmt) {
println!("{:?}", res);
}

关于rust - rusqlite MappedRows 的返回类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42594102/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com