如何将 Rust 函数作为参数传递? [英] How do you pass a Rust function as a parameter?

查看:97
本文介绍了如何将 Rust 函数作为参数传递?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以将函数作为参数传递吗?如果没有,有什么好的选择?

我尝试了一些不同的语法,但没有找到正确的语法.我知道我可以做到这一点:

fn 示例() {让乐趣:fn(值:i32)->i32;乐趣 = fun_test;乐趣(5i32);}fn fun_test(value: i32) ->i32 {println!("{}", value);价值}

但这并不是将函数作为参数传递给另一个函数:

fn fun_test(value: i32, (some_function_prototype)) ->i32 {println!("{}", value);价值}

解决方案

当然可以:

fn fun_test(value: i32, f: &dyn Fn(i32) -> i32) ->i32 {println!("{}", f(value));价值}fn 次 2(值:i32)->i32 {2 * 值}fn 主(){fun_test(5, &times2);}

因为这是 Rust,所以你必须考虑 所有权和闭包的生命周期.

TL;博士;基本上有 3 种类型的闭包(可调用对象):

  1. Fn:它不能修改它捕获的对象.
  2. FnMut:它可以修改它捕获的对象.
  3. FnOnce:最受限制的.只能调用一次,因为当它被调用时,它会消耗自身及其捕获.

请参阅闭包何时实现 Fn、FnMut 和 FnOnce?了解更多详情

如果您使用的是像闭包这样的简单的函数指针,那么捕获集是空的,并且您具有 Fn 风格.

如果你想做更多奇特的事情,那么你就必须使用 lambda 函数.

在 Rust 中有适当的函数指针,它们的工作方式与 C 中的类似.例如,它们的类型是 fn(i32) ->i32.Fn(i32) ->i32, FnMut(i32) ->i32FnOnce(i32) ->i32 实际上是特征.指向函数的指针总是实现所有这三个,但 Rust 也有闭包,它可能会或可能不会转换为指向函数的指针(取决于捕获集是否为空),但它们确实实现了其中一些特征.

例如,上面的例子可以扩展:

fn fun_test_impl(value: i32, f: impl Fn(i32) -> i32) ->i32 {println!("{}", f(value));价值}fn fun_test_dyn(value: i32, f: &dyn Fn(i32) -> i32) ->i32 {println!("{}", f(value));价值}fn fun_test_ptr(value: i32, f: fn(i32) -> i32) ->i32 {println!("{}", f(value));价值}fn 次 2(值:i32)->i32 {2 * 值}fn 主(){让 y = 2;//静态调度fun_test_impl(5, times2);fun_test_impl(5, |x| 2*x);fun_test_impl(5, |x| y*x);//动态调度fun_test_dyn(5, &times2);fun_test_dyn(5, &|x| 2*x);fun_test_dyn(5, &|x| y*x);//类似C的函数指针fun_test_ptr(5, times2);fun_test_ptr(5, |x| 2*x);//ok: 空捕获集fun_test_ptr(5, |x| y*x);//错误:预期的 fn 指针,发现闭包}

Can I pass a function as a parameter? If not, what is a good alternative?

I tried some different syntaxes but I have not found the right one. I know I can do this:

fn example() {
    let fun: fn(value: i32) -> i32;
    fun = fun_test;
    fun(5i32);
}

fn fun_test(value: i32) -> i32 {
    println!("{}", value);
    value
}

but that's not passing the function as a parameter to another function:

fn fun_test(value: i32, (some_function_prototype)) -> i32 {
    println!("{}", value);
    value
}

解决方案

Sure you can:

fn fun_test(value: i32, f: &dyn Fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}

fn times2(value: i32) -> i32 {
    2 * value
}

fn main() {
    fun_test(5, &times2);
}

As this is Rust, you have to take into account the ownership and lifetime of the closure.

TL;DR; Basically there are 3 types of closures (callable objects):

  1. Fn: It cannot modify the objects it captures.
  2. FnMut: It can modify the objects it captures.
  3. FnOnce: The most restricted. Can only be called once because when it is called it consumes itself and its captures.

See When does a closure implement Fn, FnMut and FnOnce? for more details

If you are using a simple pointer-to-function like closure, then the capture set is empty and you have the Fn flavor.

If you want to do more fancy stuff, then you will have to use lambda functions.

In Rust there are proper pointers to functions, that work just like those in C. Their type is for example fn(i32) -> i32. The Fn(i32) -> i32, FnMut(i32) -> i32 and FnOnce(i32) -> i32 are actually traits. A pointer to a function always implements all three of these, but Rust also has closures, that may or may not be converted to pointers (depending on whether the capture set is empty) to functions but they do implement some of these traits.

So for example, the example from above can be expanded:

fn fun_test_impl(value: i32, f: impl Fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}
fn fun_test_dyn(value: i32, f: &dyn Fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}
fn fun_test_ptr(value: i32, f: fn(i32) -> i32) -> i32 {
    println!("{}", f(value));
    value
}

fn times2(value: i32) -> i32 {
    2 * value
}

fn main() {
    let y = 2;
    //static dispatch
    fun_test_impl(5, times2);
    fun_test_impl(5, |x| 2*x);
    fun_test_impl(5, |x| y*x);
    //dynamic dispatch
    fun_test_dyn(5, &times2);
    fun_test_dyn(5, &|x| 2*x);
    fun_test_dyn(5, &|x| y*x);
    //C-like pointer to function
    fun_test_ptr(5, times2);
    fun_test_ptr(5, |x| 2*x); //ok: empty capture set
    fun_test_ptr(5, |x| y*x); //error: expected fn pointer, found closure
}

这篇关于如何将 Rust 函数作为参数传递?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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