【问题标题】:How to use a reference to a FnOnce closure?如何使用对 FnOnce 闭包的引用?
【发布时间】:2019-02-02 09:20:45
【问题描述】:

我有一个需要递归传递闭包参数的函数

use std::cell::RefCell;
use std::rc::Rc;

pub struct TreeNode {
    val: i32,
    left: Option<Rc<RefCell<TreeNode>>>,
    right: Option<Rc<RefCell<TreeNode>>>,
}

pub fn pre_order<F>(root: Option<Rc<RefCell<TreeNode>>>, f: F)
where
    F: FnOnce(i32) -> (),
{
    helper(&root, f);

    fn helper<F>(root: &Option<Rc<RefCell<TreeNode>>>, f: F)
    where
        F: FnOnce(i32),
    {
        match root {
            Some(node) => {
                f(node.borrow().val);
                helper(&node.borrow().left, f);
                helper(&node.borrow().right, f);
            }
            None => return,
        }
    }
}

这不起作用:

error[E0382]: use of moved value: `f`
  --> src/lib.rs:23:45
   |
22 |                 f(node.borrow().val);
   |                 - value moved here
23 |                 helper(&node.borrow().left, f);
   |                                             ^ value used here after move
   |
   = note: move occurs because `f` has type `F`, which does not implement the `Copy` trait

error[E0382]: use of moved value: `f`
  --> src/lib.rs:24:46
   |
23 |                 helper(&node.borrow().left, f);
   |                                             - value moved here
24 |                 helper(&node.borrow().right, f);
   |                                              ^ value used here after move
   |
   = note: move occurs because `f` has type `F`, which does not implement the `Copy` trait

如果我尝试将 f 的类型从 f: F 更改为 f: &amp;F,我会收到编译器错误

error[E0507]: cannot move out of borrowed content
  --> src/lib.rs:22:17
   |
22 |                 f(node.borrow().val);
   |                 ^ cannot move out of borrowed content

我该如何解决这个问题?

我这样调用函数:

let mut node = TreeNode::new(15);
node.left = Some(Rc::new(RefCell::new(TreeNode::new(9))));

let node_option = Some(Rc::new(RefCell::new(node)));
pre_order(node_option, |x| {
    println!("{:?}", x);
});

【问题讨论】:

  • F 是否需要为FnOnce() 类型?如果将其更改为输入Fn() 并使helper() 接受&amp;F,它将起作用。 F:FnOnce() 使闭包 F 按值移动到调用中,因此只能使用一次。

标签: rust closures


【解决方案1】:

FnOnce 是最通用的函数约束。但是,这意味着您的代码必须适用于所有可能的功能,包括那些使用其环境的功能。这就是为什么它被称为FnOnce:你唯一知道的是它可以被称为它至少一次 - 但不一定更多。在pre_order 内部,我们只能假设每一种可能F 都是真的:它可以被调用一次。

如果您将其更改为FnFnMut,以排除消耗其环境的闭包,您将能够多次调用它。 FnMut 是下一个最通用的函数特征,因此最好接受它而不是 Fn,以确保您可以接受大多数函数:

pub fn pre_order<F>(root: Option<Rc<RefCell<TreeNode>>>, mut f: F)
where
    F: FnMut(i32),
{
    helper(&root, &mut f);

    fn helper<F>(root: &Option<Rc<RefCell<TreeNode>>>, f: &mut F)
    where
        F: FnMut(i32),
    {
        match root {
            Some(node) => {
                f(node.borrow().val);
                helper(&node.borrow().left, f);
                helper(&node.borrow().right, f);
            }
            None => return,
        }
    }
}

【讨论】:

  • 你至少可以调用一次 - 但不一定更多 — 同意,我明白你的意思 :) F:FnOnce() + Copy
  • @cotigao FnOnce() + Copy 不包括需要改变其环境的非移动闭包。鉴于 OP 示例中的函数类型没有返回值,他可能希望它们具有副作用。
猜你喜欢
  • 2022-09-24
  • 2015-07-22
  • 1970-01-01
  • 2019-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-18
相关资源
最近更新 更多