我会将答案分为两部分,第一部分是关于如何修正返回类型而不考虑借用检查器,第二部分是为什么即使您修复返回类型也不起作用。
§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
转换为函数。我们可以命名一个函数指针类型,而不需要“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特质”,因为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,看起来终身elision不能与impl Trait一起使用)
如果你被Stable Rust卡住了,你不能使用“impl Trait”功能。你可以换性状对象在一个盒子里,在堆分配和动态调度的代价:
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<F>
实际上是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);
}
非常感谢您的详细解答。 – simao