我正在尝试编写一个返回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,但是我得到了相同的编译器错误。
发布于 2017-03-04 10:30:13
我将把答案分为两部分,第一部分是关于如何在不考虑借入检查的情况下修复返回类型,第二部分是为什么即使修复了返回类型也不能工作。
§1.
每个闭包都有一个唯一的匿名类型,因此c不能是调用方提供的任何类型的F。这意味着这行将永远不会编译:
let c: F = |row: &Row| { ... } // no, wrong, always.相反,类型应该从dump函数中传播出去,例如:
// ↓ no generics
pub fn dump(&self) -> MappedRows<“type of that c”> {
..
}稳定锈蚀并没有提供命名该类型的方法。但我们可以在夜间使用"impl特质“特性:
#![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转换为函数。我们可以命名一个函数指针类型,而不需要“推动特性”。
// ↓~~~~~~~~~~~~~~~~~~~~~~~~
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特性“,因为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特性)
如果你陷入稳定锈蚀,你不能使用“推动特性”功能。您可以将属性对象包装在一个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,则不起作用。由于生存期问题,编译器将不允许上述操作:
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实际上是MappedRows<'stmt, F>,这种类型只有在原始SQLite语句对象(具有'stmt生存期)超过它时才有效--因此编译器在返回函数时抱怨stmt死了。
实际上,如果在迭代这些行之前删除了该语句,我们将得到垃圾结果。坏的!
我们需要做的是确保在删除语句之前读取所有行。
您可以将行收集到向量中,从而将结果与语句分离,代价是将所有内容存储在内存中:
// ↓~~~~~~~~~~~~~~~~~~~~~~~~~
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);
}发布于 2017-03-04 10:31:18
这里的问题是,您正在隐式地尝试返回一个闭包,因此要找到解释和示例,您可以搜索它。
泛型<F>的使用意味着调用方决定F和的具体类型,而不是函数dump。
相反,您想要达到的目标需要期待已久的特性impl trait。
https://stackoverflow.com/questions/42594102
复制相似问题