递归关闭作为函数参数“由于需求冲突而无法推断出适当的寿命”

时间:2018-10-03 03:25:07

标签: rust

这个问题比Closure as function parameter “cannot infer an appropriate lifetime due to conflicting requirements”更复杂。

有一个递归闭包,将环境变量移入其中。

下面的代码有效,tool是实用编程的有用功能的抓包,其中包括making recursive closure

extern crate tool;
use tool::prelude::*;
use std::cell::Cell;

fn main() {
    let a = Cell::new(false);

    let fib = fix(move |f, x| {
        a.set(true);
        if x == 0 || x == 1 {
            x
        } else {
            // `f` is `fib`
            f(x - 1) + f(x - 2)
        }
    });

    println!("{}", fib(10));
}

我想知道是否可以将该闭包传递给函数,然后在该闭包中调用该函数,下面的代码将引发错误。

extern crate tool;
use tool::prelude::*;
use std::cell::RefCell;

fn main() {
    let a = RefCell::new(false);

    let fib = fix(move |f, x| {
        *a.borrow_mut() = true;
        if x == 0 || x == 1 {
            x
        } else {
            // `f` is `fib`
            b(Box::new(f), x - 1) + f(x - 2)
        }
    });

    fn b (c: Box<Fn(u64) -> u64>, n: u64) -> u64 {
        c(n)
    }

    println!("{}", b(Box::new(fib), 10));
}
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:14:24
   |
14 |             b(Box::new(f), x - 1) + f(x - 2)
   |                        ^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 8:19... 
  --> src/main.rs:8:19
   |
8  |       let fib = fix(move |f, x| {
   |  ___________________^
9  | |         *a.borrow_mut() = true;
10 | |         if x == 0 || x == 1 {
11 | |             x
...  |
15 | |         }
16 | |     });
   | |_____^
   = note: ...so that the expression is assignable:
           expected &dyn std::ops::Fn(u64) -> u64
              found &dyn std::ops::Fn(u64) -> u64
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected std::boxed::Box<(dyn std::ops::Fn(u64) -> u64 + 'static)>
              found std::boxed::Box<dyn std::ops::Fn(u64) -> u64>

1 个答案:

答案 0 :(得分:1)

您似乎在这里混几个概念。首先,您必须了解它们之间的区别:

  1. fn(A) -> B
  2. impl Fn(A) -> BT: Fn(A) -> B
  3. &dyn Fn(A) -> B
  4. Box<dyn Fn(A) -> B>

数字1是指向函数的指针的类型,就像在C语言中一样。

数字2是实现 function 特征Fn的泛型类型,即可调用的类型。

数字3是对可调用对象的动态引用(dyn关键字是可选的)。

数字4是一个特征对象,它是一个装箱的可调用对象,其真实类型已删除。

现在看看tool::fix的定义:

pub fn fix<A, B, F>(f: F) -> impl Fn(A) -> B 
where
    F: Fn(&Fn(A) -> B, A) -> B, 

由此可见,fix使用数字2表示f,而数字3使用A参数。而且,它返回数字2。

这里最棘手的部分是f是一个以函数为参数的函数。 f本身可以是实现f的任何类型,但是该函数的第一个参数必须为Fn类型。

您的原始错误来自试图装箱&dyn Fn的问题,但是您不能一般地这样做,因为这样的值可能包含引用,并且&dyn Fn(A) -> B需要使用Box类型。

但是考虑到所有这些,您可以不用使用'static来仔细地编写函数,因此问题就消失了,结果更好了(playground):

Box