【问题标题】:What is the correct return type of a function returning an iterator produced with a closure borrowing self返回使用闭包借用 self 生成的迭代器的函数的正确返回类型是什么
【发布时间】:2021-03-21 09:51:17
【问题描述】:

考虑以下代码,强调children_iter()

use std::collections::HashMap;

type NodeMap = HashMap<i32, Node>;

struct Node {
    id: i32,
    parent: Option<i32>,
    sibling: Option<i32>,
    children: Vec<i32>,
    content: String,
}

pub struct NodeIterator<'a> {
    nodes: &'a NodeMap,
    current: &'a Node,
}

impl<'a> NodeIterator<'a> {
    fn new(node: &'a Node, nodes: &'a NodeMap) -> NodeIterator<'a> {
        NodeIterator {
            nodes,
            current: node,
        }
    }

    pub fn children_iter(&self) -> impl Iterator<Item = NodeIterator> {
        self.current
            .children
            .iter()
            .map(|i| self.nodes.get(i).unwrap())
            .map(|n| Self::new(n, self.nodes))
    }
}

此代码的编译失败:

error[E0373]: closure may outlive the current function, but it borrows `self`, which is owned by the current function
  --> src/lib.rs:30:18
   |
30 |             .map(|i| self.nodes.get(i).unwrap())
   |                  ^^^ ---- `self` is borrowed here
   |                  |
   |                  may outlive borrowed value `self`
   |
note: closure is returned here
  --> src/lib.rs:26:36
   |
26 |     pub fn children_iter(&self) -> impl Iterator<Item = NodeIterator> {
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `self` (and any other referenced variables), use the `move` keyword
   |
30 |             .map(move |i| self.nodes.get(i).unwrap())
   |                  ^^^^^^^^

error[E0373]: closure may outlive the current function, but it borrows `self`, which is owned by the current function
  --> src/lib.rs:31:18
   |
31 |             .map(|n| Self::new(n, self.nodes))
   |                  ^^^              ---- `self` is borrowed here
   |                  |
   |                  may outlive borrowed value `self`
   |
note: closure is returned here
  --> src/lib.rs:26:36
   |
26 |     pub fn children_iter(&self) -> impl Iterator<Item = NodeIterator> {
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `self` (and any other referenced variables), use the `move` keyword
   |
31 |             .map(move |n| Self::new(n, self.nodes))
   |                  ^^^^^^^^

有没有办法正确指定生命周期,以便清楚地知道返回的迭代器仅在当前NodeIterator 的生命周期内有效?

【问题讨论】:

  • -&gt; impl Iterator&lt;Item = NodeIterator&gt; 没有意义你的意思是改写-&gt; impl Iterator&lt;Item = Node&gt; 吗?
  • 我不这么认为。我希望返回的迭代器生成NodeIterator 值,而不是Node,这样我就可以通过进一步调用每个生成的值上的children_iter() 继续向下递归树。也许令人困惑的是,NodeIterator 有一个额外的功能可以访问contentNode,但我把它去掉了,因为我认为它不相关。你介意解释为什么它没有意义吗?可能是我想错了。
  • 返回迭代器的迭代器有点不寻常,但我想如果它有效,那么它就有效。更常见和标准的方法是编写一个迭代整个数据结构的迭代器。

标签: rust closures lifetime borrow-checker borrowing


【解决方案1】:

您收到此错误的原因是因为在 Rust 中迭代器是 惰性求值。看起来您的闭包将在 children_iter 函数内执行,但实际上直到调用者在返回的迭代器上调用 next 方法时才会执行它们。

编译器建议使用move 关键字将借用的引用移动到解决问题的闭包中。但是,您也可以通过急切地评估您的迭代器并将结果收集到Vec&lt;NodeIterator&gt; 中来解决问题。这是固定和编译示例中的两种方法:

use std::collections::HashMap;

type NodeMap = HashMap<i32, Node>;

struct Node {
    id: i32,
    parent: Option<i32>,
    sibling: Option<i32>,
    children: Vec<i32>,
    content: String,
}

pub struct NodeIterator<'a> {
    nodes: &'a NodeMap,
    current: &'a Node,
}

impl<'a> NodeIterator<'a> {
    fn new(node: &'a Node, nodes: &'a NodeMap) -> NodeIterator<'a> {
        NodeIterator {
            nodes,
            current: node,
        }
    }

    pub fn children_iter(&self) -> impl Iterator<Item = NodeIterator> {
        self.current
            .children
            .iter()
            // lazily-evaluted closures
            // they run in the caller's scope
            .map(move |i| self.nodes.get(i).unwrap())
            .map(move |n| Self::new(n, self.nodes))
    }
    
    pub fn children_iter_vec(&self) -> Vec<NodeIterator> {
        self.current
            .children
            .iter()
            // eagerly-evaluted closures
            // we run them in the current scope
            .map(|i| self.nodes.get(i).unwrap())
            .map(|n| Self::new(n, self.nodes))
            .collect()
    }
}

playground

【讨论】:

    猜你喜欢
    • 2015-06-03
    • 1970-01-01
    • 2013-09-23
    • 1970-01-01
    • 2017-09-25
    • 1970-01-01
    • 1970-01-01
    • 2020-11-23
    • 1970-01-01
    相关资源
    最近更新 更多