2017-03-04 73 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