我具有以下高阶函数
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 {
我可以将其写为宏并通过它,但是我很好奇是否有一种方法可以不通过宏路由来做到这一点。
答案 0 :(得分:6)
您正在使用很多引用,这些引用似乎不是必需的,因此很难一一理解:
fn
已经 一个 pointer 函数,因此您可以按值传递它们,而不必使用另一层引用。因为函数指针为'static
,所以更容易。&f64
是不可变的,因此可以在不更改逻辑的情况下用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?)。