首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >rusqlite的返回类型

rusqlite的返回类型
EN

Stack Overflow用户
提问于 2017-03-04 08:58:29
回答 2查看 1.7K关注 0票数 2

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

代码语言:javascript
复制
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()
}

我被一个编译器错误困住了:

代码语言:javascript
复制
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,但是我得到了相同的编译器错误。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-03-04 10:30:13

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

§1.

每个闭包都有一个唯一的匿名类型,因此c不能是调用方提供的任何类型的F。这意味着这行将永远不会编译:

代码语言:javascript
复制
let c: F = |row: &Row| { ... } // no, wrong, always.

相反,类型应该从dump函数中传播出去,例如:

代码语言:javascript
复制
//         ↓ no generics
pub fn dump(&self) -> MappedRows<“type of that c”> {
    ..
}

稳定锈蚀并没有提供命名该类型的方法。但我们可以在夜间使用"impl特质“特性:

代码语言:javascript
复制
#![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转换为函数。我们可以命名一个函数指针类型,而不需要“推动特性”。

代码语言:javascript
复制
//                               ↓~~~~~~~~~~~~~~~~~~~~~~~~
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,那么只需要这样说就更合适了:

代码语言:javascript
复制
#![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中,代价是堆分配和动态调度:

代码语言:javascript
复制
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,则不起作用。由于生存期问题,编译器将不允许上述操作:

代码语言:javascript
复制
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死了。

实际上,如果在迭代这些行之前删除了该语句,我们将得到垃圾结果。坏的!

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

您可以将行收集到向量中,从而将结果与语句分离,代价是将所有内容存储在内存中:

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

或者反转控件,让dump接受一个函数,dump将在保持stmt活动的同时调用该函数,代价是使调用语法变得奇怪:

代码语言:javascript
复制
//                    ↓~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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分解成两个函数,让调用方保持语句活动,代价是向用户公开一个中间构造:

代码语言:javascript
复制
#![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);
}
票数 9
EN

Stack Overflow用户

发布于 2017-03-04 10:31:18

这里的问题是,您正在隐式地尝试返回一个闭包,因此要找到解释和示例,您可以搜索它。

泛型<F>的使用意味着调用方决定F的具体类型,而不是函数dump

相反,您想要达到的目标需要期待已久的特性impl trait

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42594102

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档