我刚刚开始了Rust教程,最后使用递归编写了这样的代码

extern crate rand;

use std::io;
use rand::Rng;
use std::cmp::Ordering;
use std::str::FromStr;
use std::fmt::{Display, Debug};

fn try_guess<T: Ord>(guess: T, actual: T) -> bool {
    match guess.cmp(&actual) {
        Ordering::Less => {
            println!("Too small");
            false
        }
        Ordering::Greater => {
            println!("Too big");
            false
        }
        Ordering::Equal => {
            println!("You win!");
            true
        }
    }
}

fn guess_loop<T: Ord + FromStr + Display + Copy>(actual: T)
    where <T as FromStr>::Err: Debug
{
    println!("PLease input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    let guess_int: T = guess.trim()
        .parse()
        .expect("Should enter integer number");

    println!("You guessed {} !", guess_int);

    if !try_guess(guess_int, actual) {
        guess_loop(actual)
    }
}

fn main() {
    println!("Guess the number!!!");

    let secret_number = rand::thread_rng().gen_range(1, 51);

    guess_loop(secret_number);

}

我希望从guess_loop函数中计算出递归,并引入了一个定点运算符:

fn guess_loop<T: Ord + FromStr + Display + Copy>(actual: T, recur: fn(T) -> ()) -> ()
    where <T as FromStr>::Err: Debug
{
    println!("PLease input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    let guess_int: T = guess.trim()
        .parse()
        .expect("Should enter integer number");

    println!("You guessed {} !", guess_int);

    if !try_guess(guess_int, actual) {
        recur(actual)
    }
}

fn fix<T, R>(func: fn(T, fn(T) -> R) -> R) -> fn(T) -> R {
    fn fixed(val: T) -> R {
        func(val, fixed)
    }
    fixed
}

fn main() {
    println!("Guess the number!!!");

    let secret_number = rand::thread_rng().gen_range(1, 51);

    fix(guess_loop)(secret_number);
}

但这导致了许多错误,比如

error[E0401]: can't use type parameters from outer function; try using a local type parameter instead
  --> src/main.rs:49:19
   |
49 |     fn fixed(val: T) -> R {
   |                   ^ use of type variable from outer function

error[E0401]: can't use type parameters from outer function; try using a local type parameter instead
  --> src/main.rs:49:25
   |
49 |     fn fixed(val: T) -> R {
   |                         ^ use of type variable from outer function

error[E0434]: can't capture dynamic environment in a fn item; use the || { ... } closure form instead
  --> src/main.rs:50:9
   |
50 |         func(val, fixed)
   |         ^^^^

我的下一次try 是将guess_loop的定义改为

fn guess_loop<T: Ord + FromStr + Display + Copy, F>(actual: T, recur: F) -> ()
where <T as FromStr>::Err: Debug,
      F: Fn(T) -> ()
{ ... }

并将fix重新定义为

fn fix<T, R, F>(func: fn(T, F) -> R) -> F
    where F: Fn(T) -> R
{
    let fixed = |val: T| func(val, fix(func));
    fixed
}

这导致了

error[E0308]: mismatched types
  --> src/main.rs:53:5
   |
53 |     fixed
   |     ^^^^^ expected type parameter, found closure
   |
   = note: expected type `F`
   = note:    found type `[closure@src/main.rs:52:17: 52:46 func:_]`

error: the type of this value must be known in this context
  --> src/main.rs:61:5
   |
61 |     fix(guess_loop)(secret_number);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

如何编写类似的fix函数?

推荐答案

首先,变量名在初始化之后才存在.你不能让fixed人这样称呼自己.

其次,不能通过函数period的值返回闭包.泛型参数由caller Select ,调用方不知道函数中的闭包类型.

我并不是说下面的方法是最好的方法,但这是我能想到的最简单的类型判断方法.

fn guess_loop<T>(actual: T, recur: &Fn(T)) -> ()
    where T: Ord + FromStr + Display + Copy,
          <T as FromStr>::Err: Debug
{
    // ...
}

fn fix<T, R, F>(func: F) -> Box<Fn(T) -> R>
    where T: 'static,
          R: 'static,
          F: Fn(T, &Fn(T) -> R) -> R + 'static
{
    use std::cell::RefCell;
    use std::rc::Rc;

    let fixed = Rc::new(RefCell::new(None));
    let fixed_fn = {
        let fixed = fixed.clone();
        move |val: T| -> R {
            let fixed_ref = fixed.borrow();
            let fixed_ref: &Box<_> = fixed_ref.as_ref().unwrap();
            func(val, &**fixed_ref)
        }
    };
    *fixed.borrow_mut() = Some(Box::new(fixed_fn));

    Box::new(move |val: T| -> R {
        let fixed_ref = fixed.borrow();
        let fixed_ref: &Box<_> = fixed_ref.as_ref().unwrap();
        fixed_ref(val)
    })
}

为了让fixed_fn指代它自己,我们必须创造一些东西,让它从before中读出它的存在.不幸的是,这意味着要有一个循环,并且要有hates个循环.因此,我们通过构造一个以None开头的引用计数RefCell<Option<_>>来实现这一点,它将在稍后进行变异,以包含定点闭包.

其次,我们不能将这个句柄用作可调用的句柄,所以我们必须显式地拉出一个指向闭包的指针,以便将其传递给func.

第三,编译器似乎无法正确推断fixed的类型.我希望它能算出它是Rc<RefCell<Option<{closure}>>>,但它拒绝这么做.因此,我们不得不求助于存储一个Box<Fn(T) -> R>,因为我们不能明确地命名闭包的类型.

最后,我们必须构造一个new闭包,将第二个句柄设为fixed,将其解包并调用它.同样,我们不能直接使用fixed作为可调用项.我们也不能重复使用闭包insidefixed,因为要做到这一点,我们必须把它放在它自己的Rc中,在这一点上,事情开始变得疯狂.

... more个疯子.

最后,我们必须在Box中返回第二个闭包,因为正如我之前所说的,我们不能按值返回闭包,因为我们不能在签名中命名它们的类型.

*deep breath*

如果有人有更简单的解决方案,我很乐意看到P

Rust相关问答推荐

有条件默认实现

如何从使用mockall模拟的方法中返回self?

将内部类型作为参数的泛型 struct 上的方法

如何访问Rust存储值的内存地址

使用pyo3::Types::PyIterator的无限内存使用量

为什么我可以跟踪以前borrow 过的变量?房主在哪里?

如何导入crate-type=[";cdylib;]库?

为昂贵的for循环制作筛子

如何将单个 struct 实例与插入器一起传递到Rust中的映射

在Rust中,Box:ed struct 与普通 struct 在删除顺序上有区别吗?

为什么AsyncRead在Box上的实现有一个Unpin特征绑定?

如何将生存期参数添加到框<>;具有dyn类型别名

仅在使用 &mut 或线程时borrow 的数据在闭包之外转义?

返回优化后的标题:返回异步块的闭包的类型擦除

Rust: 目标成员属于哪个"目标家族"的列表是否存在?

Rust 将特性传递给依赖项

SDL2 没有在终端键上触发?

`map` 调用在这里有什么用吗?

有没有办法在 Rust 中对 BigInt 进行正确的位移?

如何制作具有关联类型的特征的类型擦除版本?