我有以下高阶函数
fn ensure_tonicty(tone_fn: &fn(&f64, &f64) -> bool) -> impl Fn(&Vec<f64>) -> bool {
return |floats: &Vec<f64>| -> bool {
let first = floats.first().unwrap();
let rest = &floats[1..];
fn f(tone_fn: &fn(&f64, &f64) -> bool, prev: &f64, xs: &[f64]) -> bool {
match xs.first() {
Some(x) => tone_fn(prev, x) && f(tone_fn, x, &xs[1..]),
None => true,
}
};
return f(tone_fn, first, rest);
};
}
我的目标是返回这个 lambda。不过,我不知道如何在这里有效地使用 tone_fn
。
上面的代码出错了:
error[E0621]: explicit lifetime required in the type of `tone_fn`
--> src/lib.rs:1:56
|
1 | fn ensure_tonicty(tone_fn: &fn(&f64, &f64) -> bool) -> impl Fn(&Vec<f64>) -> bool {
| ----------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'static` required
| |
| help: add explicit lifetime `'static` to the type of `tone_fn`: `&'static for<'r, 's> fn(&'r f64, &'s f64) -> bool`
如果我尝试包括生命周期,我不确定如何键入 impl Fn
,并包括生命周期
// where do I write `'a`?
fn ensure_tonicty<'a>(tone_fn: &'a fn(&f64, &f64) -> bool) -> impl Fn(&Vec<f64>) -> bool {
我可以把它写成一个宏并通过它,但我很好奇是否有一种方法可以在不走宏路线的情况下做到这一点。
最佳答案
您正在使用很多 引用资料,这似乎不是必需的,并且让您更难弄清楚这一切:
- A
fn
已经是一个函数指针,因此您可以按值传递它们,而不是使用另一层引用。这更容易,因为函数指针是'static
. - 所有这些
&f64
s 是不可变的,因此可以替换为f64
不改变逻辑。这应该与使用引用的速度相同(或者可能更快)。
一旦你这样做了,你将没有太多引用,并且会更清楚是什么导致了问题:
fn ensure_tonicty(tone_fn: fn(f64, f64) -> bool) -> impl Fn(&Vec<f64>) -> bool {
|floats: &Vec<f64>| -> bool {
let first = *floats.first().unwrap();
let rest = &floats[1..];
fn f(tone_fn: fn(f64, f64) -> bool, prev: f64, xs: &[f64]) -> bool {
match xs.first() {
Some(&x) => tone_fn(prev, x) && f(tone_fn, x, &xs[1..]),
None => true,
}
};
f(tone_fn, first, rest);
};
}
现在,错误是:
error[E0373]: closure may outlive the current function, but it borrows `tone_fn`, which is owned by the current function
--> src/lib.rs:2:12
|
2 | return |floats: &Vec<f64>| -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `tone_fn`
...
11 | return f(tone_fn, first, rest);
| ------- `tone_fn` is borrowed here
|
note: closure is returned here
--> src/lib.rs:2:12
|
2 | return |floats: &Vec<f64>| -> bool {
| ____________^
3 | | let first = *floats.first().unwrap();
4 | | let rest = &floats[1..];
5 | | fn f(tone_fn: fn(f64, f64) -> bool, prev: f64, xs: &[f64]) -> bool {
... |
11 | | return f(tone_fn, first, rest);
12 | | };
| |_____^
help: to force the closure to take ownership of `tone_fn` (and any other referenced variables), use the `move` keyword
|
2 | return move |floats: &Vec<f64>| -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help
部分告诉您如何修复它:关闭 move
它的环境。结果是:
fn ensure_tonicty(tone_fn: fn(f64, f64) -> bool) -> impl Fn(&[f64]) -> bool {
move |floats: &[f64]| -> bool {
let first = floats[0];
let rest = &floats[1..];
fn f(tone_fn: fn(f64, f64) -> bool, prev: f64, xs: &[f64]) -> bool {
match xs.first() {
Some(&x) => tone_fn(prev, x) && f(tone_fn, x, &xs[1..]),
None => true,
}
};
f(tone_fn, first, rest)
}
}
如果您从另一个函数返回一个闭包,您几乎总是需要这个关键字。否则,闭包中提到的任何变量都将引用函数结束时将超出范围的值。使用 move
关键字会移动这些值,因此它们会随闭包移动。
另请注意我所做的其他更改,以使代码更加地道:
- 使用表达式代替
return
关键字。 - 使用
&[f64]
而不是&Vec<f64>
在函数参数中(参见 Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument? )。
关于recursion - 在 Rust 中返回一个递归闭包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55748962/