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

时间:2018-03-17 13:27:04

标签: generics rust traits

我正在尝试创建一个通用函数来从stdin中读取一个数字:

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`

1 个答案:

答案 0 :(得分:3)

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

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>要求类型实现std::error::Error,但特征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,
    
相关问题