2017-03-04 42 views
0

我想写一个返回rusqlite::MappedRows的方法:为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 `[[email protected]/main.rs:70:20: 73:10]` 

我在做什么错在这里?

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

回答

3

我会将答案分为两部分,第一部分是关于如何修正返回类型而不考虑借用检查器,第二部分是为什么即使您修复返回类型也不起作用。


§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); 
} 
+0

非常感谢您的详细解答。 – simao

1

这里的问题是,你是隐试图返回封闭,所以要找到解释和例子,你可以搜索。

使用通用<F>意味着调用者决定具体类型F功能dump的。

你想达到什么需要期待已久的功能impl trait