具有特征的通用函数,用于读取抱怨错误特征的数字

Generic function with traits to read a number complaining about Error trait

我正在尝试创建一个通用函数来从标准输入读取数字:

use std::error::Error;
use std::io;
use std::io::Write;
use std::str::FromStr;

fn read_number<F: FromStr>(prompt: &str) -> Result<F, Box<Error>> {
    let mut guess = String::new();
    let mut sout = io::stdout();

    sout.write(prompt.as_bytes())?;

    sout.flush()?;

    io::stdin().read_line(&mut guess)?;

    Ok(guess.trim().parse()?)
}

fn read_until_number<F: FromStr>(prompt: &str) -> F {
    loop {
        match read_number(prompt) {
            Ok(num) => break num,
            Err(_) => println!("Please enter valid number."),
        };
    }
}

fn main() {
    let x: u32 = read_until_number("Enter integer:\n");
    let y: f32 = read_until_number("Enter float:\n");
    println!("You entered this integer: {} and this float: {}", x, y);
}

没用;我收到以下错误:

error[E0277]: the trait bound `<F as std::str::FromStr>::Err: std::error::Error` is not satisfied
  --> src/main.rs:16:8
   |
16 |     Ok(guess.trim().parse()?)
   |        ^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `<F as std::str::FromStr>::Err`
   |
   = help: consider adding a `where <F as std::str::FromStr>::Err: std::error::Error` bound
   = note: required because of the requirements on the impl of `std::convert::From<<F as std::str::FromStr>::Err>` for `std::boxed::Box<std::error::Error>`
   = note: required by `std::convert::From::from`

错误消息告诉您该怎么做:

consider adding a `where <F as std::str::FromStr>::Err: std::error::Error` bound

遵循建议,但使用更简单的语法:

fn read_number<F>(prompt: &str) -> Result<F, Box<Error>>
where
    F: FromStr,
    F::Err: std::error::Error,

这会导致另一个错误,该错误还会告诉您该怎么做:

error[E0310]: the associated type `<F as std::str::FromStr>::Err` may not live long enough
  --> src/main.rs:20:8
   |
20 |     Ok(guess.trim().parse()?)
   |        ^^^^^^^^^^^^^^^^^^^^^
   |
   = help: consider adding an explicit lifetime bound `<F as std::str::FromStr>::Err: 'static`...
note: ...so that the type `<F as std::str::FromStr>::Err` will meet its required lifetime bounds
  --> src/main.rs:20:8
   |
20 |     Ok(guess.trim().parse()?)
   |        ^^^^^^^^^^^^^^^^^^^^^

遵循建议并将其添加到我们现有的限制中:

fn read_number<F>(prompt: &str) -> Result<F, Box<Error>>
where
    F: FromStr,
    F::Err: std::error::Error + 'static,

然后您将得到与 read_until_number 函数相同的错误。重复同样的过程,你最终得到:

use std::error::Error;
use std::io;
use std::io::Write;
use std::str::FromStr;

fn read_number<F>(prompt: &str) -> Result<F, Box<Error>>
where
    F: FromStr,
    F::Err: std::error::Error + 'static,
{
    let mut guess = String::new();
    let mut sout = io::stdout();

    sout.write(prompt.as_bytes())?;

    sout.flush()?;

    io::stdin().read_line(&mut guess)?;

    Ok(guess.trim().parse()?)
}

fn read_until_number<F>(prompt: &str) -> F
where
    F: FromStr,
    F::Err: std::error::Error + 'static,
{
    loop {
        match read_number(prompt) {
            Ok(num) => break num,
            Err(_) => println!("Please enter valid number."),
        };
    }
}

fn main() {
    let x: u32 = read_until_number("Enter integer:\n");
    let y: f32 = read_until_number("Enter float:\n");
    println!("You entered this integer: {} and this float: {}", x, y);
}

为什么需要这个?

  1. implementation of From for Box<Error> requires that the type implement std::error::Error, but the trait FromStr 对关联的 Err 类型没有限制。您必须将限制添加到您的函数才能执行转换。

  2. 默认情况下,参数位置中的特征对象具有隐式 'static 绑定,就好像您已经完成 Box<Error + 'static> 一样。您 可以 更改 read_number 以使用更细微的生命周期,但您无法更改 read_until_number 因为错误必须存在于函数之外:

    fn read_number<'a, F>(prompt: &'a str) -> Result<F, Box<Error + 'a>>
    where
        F: FromStr,
        F::Err: std::error::Error + 'a,