【问题标题】:How to return a reference when implementing an iterator?实现迭代器时如何返回引用?
【发布时间】:2021-10-06 21:56:45
【问题描述】:

我想返回一个对集合中的拥有对象的引用(即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 不允许其 Items 的生命周期随 self 的生命周期而变化,但在您的情况下,这些项目 存储 在迭代器并共享它的生命周期,所以这是行不通的。 Vec 与您尝试做的事情完全不同。迭代器应该与容器分开。

标签: rust iterator lifetime


【解决方案1】:

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

鉴于Iterator 特征的设计,您不能让迭代器拥有其数据并且将其Item 作为对其的引用。只是没有办法表达生命。

为了使项目成为引用,通常编写迭代器的方式是让它们持有对基础数据的引用。这为数据提供了一个命名的生命周期,可以在关联的Item 上使用。 Vec 有点 这样做,但它有点不同,因为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,
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-09
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 2016-01-23
    • 2011-07-23
    • 2021-03-18
    相关资源
    最近更新 更多