实现迭代器时如何返回引用?

问题描述

我想返回对集合中拥有的对象的引用(即 Vec),但我似乎无法获得正确的生命周期。这是我第一次尝试的:

struct StringHolder {
    strings: Vec<String>,i: usize,}

impl Iterator for StringHolder {
    type Item<'a> = &'a String;
    fn next(&mut self) -> Option<Self::Item> {
        if self.i >= self.strings.len() {
            None
        } else {
            self.i += 1;
            Some(&self.strings[self.i])
        }
    }
}

fn main() {
    let sh = StringHolder { strings: vec![],i: 0 };
    for string in sh {
        println!("{}",string);
    }
}

我收到 generic associated types are unstablelifetimes do not match type in trait错误。我尝试了其他一些迭代,但似乎没有任何效果

根据我读过的一些内容,我认为这可能是不可能的,但后来我似乎无法弄清楚 Vec 本身是如何做到的。例如,我可以使用以下内容简单地迭代底层 Vec 并在每次迭代时返回一个引用:


struct StringHolder {
    strings: Vec<String>,}

impl<'a> IntoIterator for &'a StringHolder {
    type Item = &'a String;
    type IntoIter = ::std::slice::Iter<'a,String>;
    fn into_iter(self) -> Self::IntoIter {
        (&self.strings).into_iter()
    }
}

fn main() {
    let sh = StringHolder { strings: vec!["A".to_owned(),"B".to_owned()] };
    for string in &sh {
        println!("{}",string);
    }
}

所以这让我觉得这是可能的,我只是还没有弄清楚生命周期。感谢您的帮助。

解决方法

Iterator 特征不包括 Item 的生命周期,这是您看到的错误之一。另一个暗指 GATs,这是一个不稳定的 Rust 特性。应用于此示例的 GAT 允许您将每次调用 next() 的项目的生命周期绑定在一起,而不是所有项目都具有相同的生命周期。话虽如此,Iterator 特征不太可能改变,因此这种更灵活的行为必须是一个新特征。

鉴于 Iterator trait 的设计,你不能让迭代器拥有它的数据并且让它的 Item 成为对它的引用。只是没有一种方式来表达生命。

迭代器通常的编写方式,为了让项目成为引用,是让它们持有对底层数据的引用。这为数据提供了命名生命周期,可用于关联的 ItemVec 有点这样做,但它有点不同,因为 Vec 实际上从 slice 获取它的迭代。

您的完整示例:

struct StringHolder {
    strings: Vec<String>,}

struct StringHolderIter<'a> {
    string_holder: &'a StringHolder,i: usize,}

impl<'a> Iterator for StringHolderIter<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<Self::Item> {
        if self.i >= self.string_holder.strings.len() {
            None
        } else {
            self.i += 1;
            Some(&self.string_holder.strings[self.i - 1])
        }
    }
}

impl<'a> IntoIterator for &'a StringHolder {
    type Item = &'a str;
    type IntoIter = StringHolderIter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        StringHolderIter {
            string_holder: self,i: 0,}
    }
}