将修改其环境的闭包传递给Rust中的函数 [英] Passing a closure that modifies its environment to a function in Rust

查看:96
本文介绍了将修改其环境的闭包传递给Rust中的函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用于捕获和修改其环境的闭包。我想将此闭包传递给接受闭包的函数:

I have a closure that captures and modifies its environment. I want to pass this closure to a function that accepts closures:

fn main() {
    let mut integer = 5;
    let mut closure_variable = || -> i32 {
        integer += 1;
        integer
    };
    execute_closure(&mut closure_variable);
}

fn execute_closure(closure_argument: &mut Fn() -> i32) {
    let result = closure_argument();
    println!("Result of closure: {}", result);
}

由于闭包修改了其环境,因此失败:

Because the closure modifies its environment, this fails:

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`
 --> src/main.rs:3:32
  |
3 |       let mut closure_variable = || -> i32 {
  |  ________________________________^
4 | |         integer += 1;
5 | |         integer
6 | |     };
  | |_____^
7 |       execute_closure(&mut closure_variable);
  |                       --------------------- the requirement to implement `Fn` derives from here
  |
note: closure is `FnMut` because it mutates the variable `integer` here
 --> src/main.rs:4:9
  |
4 |         integer += 1;
  |         ^^^^^^^

据我从何时闭包实现Fn,FnMut和FnOnce?,这意味着我的闭包实际上已扩展为实现特征 FnMut 。此特征是可变的,这意味着调用函数会更改(隐式)对象。我认为这是正确的,因为在调用 execute_closure()之后应修改变量 integer

As I understand from When does a closure implement Fn, FnMut and FnOnce?, this means that my closure actually is expanded to a struct that implements the trait FnMut. This trait is mutable, meaning calling the function changes the (implicit) object. I think this correct, because the variable integer should be modified after calling execute_closure().

我如何说服编译器还可以,并且实际上我想调用 FnMut 函数?还是在此示例中,我如何使用Rust根本上有问题?

How do I convince the compiler this is okay and that I actually want to call a FnMut function? Or is there something fundamentally wrong with how I use Rust in this example?

推荐答案

如果可以更改接受闭包的函数...



接受 FnMut 而不是 Fn

fn main() {
    let mut integer = 5;
    execute_closure(|| {
        integer += 1;
        integer
    });
}

fn execute_closure<F>(mut closure_argument: F)
where
    F: FnMut() -> i32,
{
    let result = closure_argument();
    println!("Result of closure: {}", result);
}



如果您无法更改接受闭包的函数...

使用类型为内部可变性。 Cell.html rel = nofollow noreferrer> Cell RefCell

If you can not change the function that accepts the closure...

Use interior mutability provided by types like Cell or RefCell:

use std::cell::Cell;

fn main() {
    let integer = Cell::new(5);
    execute_closure(|| {
        integer.set(integer.get() + 1);
        integer.get()
    });
}

fn execute_closure<F>(closure_argument: F)
where
    F: Fn() -> i32,
{
    let result = closure_argument();
    println!("Result of closure: {}", result);
}








或者在此示例中,我如何使用Rust根本存在错误?

Or is there something fundamentally wrong with how I use Rust in this example?

也许。类型为& mut Fn()->的参数i32 无法对其已关闭的变量进行突变,因此错误消息对我来说很有意义。

Perhaps. An argument of type &mut Fn() -> i32 cannot mutate the variables it has closed over, so the error message makes sense to me.

这是类型类似于& mut& u8 的类型-您可以更改外部引用以指向另一个不可变引用,但不能忽略内部不变性并更改

It's kind of similar to the type &mut &u8 — you could alter the outer reference to point to another immutable reference, but you cannot "ignore" the inner immutability and change the numeric value.

在旁边:

原始代码使用动态调度,因为是提供间接功能的 trait对象。在许多情况下,您会看到我上面发布的此版本,该版本使用静态分派,并且可以变体。我也已内联了闭包,因为这是正常的语法。

The original code uses dynamic dispatch because there is a trait object that provides indirection. In many cases you'd see this version that I posted above, which uses static dispatch and can be monomorphized. I've also inlined the closure as that's the normal syntax.

以下是原始版本,只需进行足够的更改即可进行操作:

Here's the original version with just enough changes to work:

fn main() {
    let mut integer = 5;
    let mut closure_variable = || -> i32 {
        integer += 1;
        integer
    };
    execute_closure(&mut closure_variable);
}

fn execute_closure(closure_argument: &mut FnMut() -> i32) {
    let result = closure_argument();
    println!("Result of closure: {}", result);
}

这篇关于将修改其环境的闭包传递给Rust中的函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆