簡體   English   中英

如何使用rust閉包創建迭代器?

[英]How do you create an iterator using a closure in rust?

我天真地試圖做到這一點:

struct Foo<'a, S: Send, T:Send> {
    next_:Box<Fn<(&'a mut S,), Option<T>> + Send>,
    state:S
}

impl<'a, S: Send, T: Send> Iterator<T> for Foo<'a, S, T> {
    fn next(&mut self) -> Option<T> {
        return self.next_.call((&mut self.state,));
    }
}

為了創建一個迭代器,我可以使用閉包輕松地發送任務。

但是,它會產生可怕的生命周期不匹配錯誤:

<anon>:8:33: 8:48 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
<anon>:8         return self.next_.call((&mut self.state,));
                                         ^~~~~~~~~~~~~~~
<anon>:7:5: 9:6 help: consider using an explicit lifetime parameter as shown: fn next(&'a mut self) -> Option<T>
<anon>:7     fn next(&mut self) -> Option<T> {
<anon>:8         return self.next_.call((&mut self.state,));
<anon>:9     }
error: aborting due to previous error
playpen: application terminated with error code 101

我不明白這個錯誤。

閉包應接受帶有生存期'a的參數,該生存期為結構的生存期。

國家歸該結構所有,因此其生存期為'a。

使用next_.call((&mut self.state,))不會調用任務。 它應該只在call()的持續時間內,據我所知應該是有效的。

因此,這里的不匹配是在next()中self的生命周期和“ a in call”之間,但是我不明白為什么它不是'a。

修復上面代碼的正確方法是什么?

是否有更好的方法可以做到這一點?

圍欄: http : //is.gd/hyNi0S

這需要較高等級的壽命 ,因為在封閉的壽命不應該是類型簽名的一部分:關閉只是想采取&'a mut S 任何一輩子'a (因為它需要調用函數的數據是僅保證next方法的內部持久:沒有外部可命名的名稱,而不是類型簽名在外部公開的生命周期(並且在某種程度上是可控的)。 這是不可能的,但是我看過Niko Matsakis談論在IRC上進行工作,並且有諸如#18837之類的准備請求請求,因此希望很快就會出現。

需要說明的是:代碼失敗是因為next_只能使用至少存在'a的引用來調用,但是&mut self.state生存期與&mut self一樣長,除非'a聲明為&'a mut self否則不會&mut self.state 'a &'a mut self (這就是編譯器建議的原因)。 添加此生存期是非法的,因為它不滿足特征聲明的要求。

您現在可以使用舊的閉包(無論如何本質上就是Fn trait對象)解決此問題,甚至還有一個標准的庫類型Unfold以為您做到這一點: Unfold

這是當前Rust特質系統的不幸限制,很快就會取消。 缺乏高尚的生命 據我所知,它的實現目前正在研究中。

讓我們更仔細地檢查您的結構定義(我刪除了mut以允許使用'static進行進一步的推理,但這並不會使它變得不太通用):

struct Foo<'a, S: Send, T:Send> {
    next_: Box<Fn<(&'a S,), Option<T>> + Send>,  // '
    state: S
}

'a生命周期參數是此處的輸入參數。 這意味着它是由結構的用戶而不是其實現者提供的。

(順便說一句,這不是結構實例的生存期-您不​​能僅使用類型參數指定此類生存期;它必須是self引用的生存期,您也不能使用,因為Iterator trait方法沒有生存期參數。不過,這只是附帶說明,與實際問題無關)

這意味着您的結構的用戶可以選擇'a 任意” ,包括選擇一些比您的結構的壽命更長的壽命,例如'static 現在觀察一下這種選擇如何改變結構(只是用'static代替'a ):

struct FooStatic<S: Send, T: Send> {
    next_: Box<Fn<(&'static S,), Option<T>> + Send>,  // '
    state: S
}

突然之間,閉包只能接受'static引用,這顯然不是您的情況-next next()方法中self生存期可能會縮短,因此您不能僅將其傳遞給閉包。 僅當self生存期確實對應於'a (編譯器建議),這才可行:

fn next(&'a mut self) -> Option<T>

但是,正如我之前所說,您不能編寫此代碼,因為它會違反特質合同。

對於更高種類的生命周期,可以在閉包本身上指定生命周期參數:

struct Foo<S: Send, T: Send> {
    next_: Box<for<'a> Fn<(&'a mut S,), Option<T>> + Send>,
    state: S
}

這樣,閉包的生命周期參數由閉包的調用者選擇,在這種情況下,它是Iterator trait的實現者(即您:)),因此可以使用任何引用(包括引用)來調用next_進入Foo內部。

您可以從itertools中使用完全滿足您需要的iterate

否則,如果要在不使用狀態概念的情況下將所有邏輯封裝在一個閉包中,則可以通過以下方式實現它:

struct IterClosure<T, C>(C) where C: FnMut() -> Option<T>;

impl<T, C> Iterator for IterClosure<T, C> where C: FnMut() -> Option<T>
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        (self.0)()
    }
}

fn iter<T>(f: impl FnMut() -> Option<T>) -> impl Iterator<Item=T> {
    IterClosure(f)
}

fn main() {
    let mut it = (0..10).into_iter();
    let mut closure = || it.next();
    println!("{}", iter(closure).sum::<i32>());

    let mut it = (0..10).into_iter();
    iter(|| it.next()).for_each(
        |i| println!("{}", i)
    )
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM