The problem

我正在为n-树构建预排序遍历,以这种方式给出一个闭包,以便为遍历中的每个 node 执行闭包.

我的第一个方法是:

#[derive(Debug)]
pub struct Node<T> {
    value: T,
    children: Vec<Node<T>>,
}

impl<T> Node<T> {
    pub fn preorder<F>(&self, mut f: F)
    where
        F: FnMut(&Self),
    {
        f(self);
        self.children.iter().for_each(|child| child.preorder(f));
    }
}

然而,在Foreach迭代器中调用child.preorder(f)语句时,我遇到了一个错误.

error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure
  --> src/lib.rs:12:62
   |
7  |     pub fn preorder<F>(&self, mut f: F)
   |                               ----- captured outer variable
...
12 |         self.children.iter().for_each(|child| child.preorder(f));
   |                                       -------                ^ move occurs because `f` has type `F`, which does not implement the `Copy` trait
   |                                       |
   |                                       captured by this `FnMut` closure

Playground

Solutions I found

将preorder方法的标头更改为以下代码可以很好地编译,但它会在要使用的闭包上设置一个我根本不想要的副本.

pub fn preorder<F>(&self, mut f: F)
    where
        F: FnMut(&Self) + Copy,

我找到的解决闭包上必需的副本的另一种方法是使用如下所示的浸入式方法(see it in the playground):

pub struct Node<T> {
    value: T,
    children: Vec<Node<T>>,
}

impl<T> Node<T> {
    fn preorder_immersion<F>(&self, f: &mut F)
    where
        F: FnMut(&Self),
    {
        f(self);
        self.children
            .iter()
            .for_each(|child| child.preorder_immersion(f));
    }

    pub fn preorder_mut<F>(&self, mut f: F)
    where
        F: FnMut(&Self),
    {
        self.preorder_immersion(&mut f);
    }
}

坦率地说,看着这一幕是痛苦的.基本上是因为它意味着每个遍历实现都有两个方法.

What I want

如果能够在调用预订单方法时执行这样的操作,那就太好了:

let mut result = Vec::new();
node.preorder(|n| result.push(*n.value()));

我给出的最新解决方案运行良好.

My question:有没有更优雅的方式呢?我是不是遗漏了什么?或者使用沉浸式方法可以吗?

推荐答案

就像我在之前的一条 comments 中所说的那样,我会 Select 基于Iterator的方法,在探索过程中,我发现了这个解决方案,在我看来,这个解决方案相当优雅:

impl<T> Node<T> {
    pub fn preorder(&self) -> impl Iterator<Item = &Self> {
        Iter::new(self)
    }
}

struct Iter<'a, T> {
    left: Vec<&'a Node<T>>,
}

impl<'a, T> Iter<'a, T> {
    fn new(root: &'a Node<T>) -> Self {
        Self { left: vec![root] }
    }
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a Node<T>;
    fn next(&mut self) -> Option<Self::Item> {
        let current = self.left.pop()?;
        self.left.extend(current.children.iter().rev());
        Some(current)
    }
}

我们将尚未产生的 node 存储为Vec,反过来,这样我们就可以使用popextend,这应该是非常有效的.每当我们产生一个Node时,我们就把它的子 node 加到剩余的 node 上.

您可以像使用node.preorder().for_each(your_closure_or_function)一样使用它,而不是像这样使用它,但是只需额外的函数调用的很小代价,您就可以获得已经围绕Rust中的迭代器构建的全部功能和工具集.

Rust相关问答推荐

使用InlineTables序列化toml ArrayOfTables

给定使用newype习语定义的类型上的铁 rust Vec,有没有方法获得底层原始类型的一部分?

为什么迭代器上的`. map(...)`的返回类型如此复杂?

rust 蚀将动力特性浇到混凝土 struct 上是行不通的

交叉术语未正确清除屏幕

为什么reqwest以文本形式下载二进制文件?

为什么我们需要std::thread::scope,如果我们可以使用thread.join()在函数的生命周期内删除引用?

RUST应用程序正在退出,错误代码为:(退出代码:0xc0000005,STATUS_ACCESS_VIOLATION)

定义只有一些字段可以缺省的 struct

通过RabbitMQ取消铁 rust 中长时间运行的人造丝任务的策略

循环访问枚举中的不同集合

允许 rust 迹 struct 条目具有多种类型

借来的价值生命周期 不够长,不确定为什么它仍然是借来的

Rust 重写函数参数

将 &str 或 String 保存在变量中

Rust编译器通过哪些规则来确保锁被释放?

有什么办法可以追踪泛型的单态化过程吗?

TcpStream::connect - 匹配武器具有不兼容的类型

深度嵌套枚举的清洁匹配臂

如何为枚举中的单个或多个值返回迭代器