读取标准输入行

时间:2016-03-28 11:41:15

标签: rust

我不知道为什么这个简单的例子不起作用:

use std::io;
use std::io::prelude::*;

fn main() {

    let stdin = io::stdin();

    for line in stdin.lock().lines() {
        match line {
            Ok(val) => match val {
                v @ _ => println!("{:?}", v),
                "q" => std::process::exit(0)
            },
            _ => println!("ERROR")
        }
    }

}

错误是:

src/main.rs:8:5: 16:6 error: type mismatch resolving `<std::io::Lines<std::io::stdio::StdinLock<'_>> as core::iter::Iterator>::Item == core::result::Result<&str, _>`:
 expected struct `collections::string::String`,
    found &-ptr [E0271]
src/main.rs: 8     for line in stdin.lock().lines() {
src/main.rs: 9         match line {
src/main.rs:10             Ok(val) => match val {
src/main.rs:11                 v @ _ => println!("{:?}", v),
src/main.rs:12                 "q" => std::process::exit(0)
src/main.rs:13             },
               ...

我只是想回显输入的stdin行,如果行等于“q”则退出应用程序。

3 个答案:

答案 0 :(得分:2)

解释

这个稍微令人困惑的错误消息是Rust执行类型推断的方法的结果。当它查看循环的第一行时,Rust不知道line具有什么类型。所以它试图弄清楚循环体:

match line {
    Ok(val) => /* ... */
    _ => /* ... /*
}

使用此信息rustc知道line的类型至少为Result<_, _>,其中_是编译器仍然不知道的类型。所以它试图找出最后一点:

Ok(val) => match val {
    v @ _ => println!("{:?}", v),
    "q" => std::process::exit(0)
},

绑定val对应Result的第一个类型参数。那么这里的val是什么。您将val"q"匹配。后一个表达式的类型为&str,因此编译器假定val的类型为&str。因此line类型Result<&str, _>_仍然未知,但现在无关紧要。)

现在rustc试图找出如何使用stdin.lock().lines()的结果作为产生Result<&str, _>的迭代器。这对编译器来说是不可能的,因为std::io::Lines总是产生Result<String, io::Error>

要获得更好的错误消息,可以显式声明迭代器的类型(在这种情况下:重新绑定它):

for line in stdin.lock().lines() {
    let line: io::Result<String> = line;
    // ...
}

这导致

 <anon>:12:17: 12:20 error: mismatched types:
  expected `collections::string::String`,
     found `&'static str`
 (expected struct `collections::string::String`,
     found &-ptr) [E0308]
 <anon>:12                 "q" => std::process::exit(0),
                           ^~~

解决方案

显然有很多解决方案。在这里你可以看到我喜欢的一个,因为它有助于rustc自己找出类型:

for line in stdin.lock().lines() {
    match line {
        Ok(val) => match val.as_str() {    // <-- note the `as_str()`
            "q" => std::process::exit(0),
            v @ _ => println!("{:?}", v),
        },
        _ => println!("ERROR")
    }
}

(我也将"q"手臂移至顶部 - 否则无法访问)

答案 1 :(得分:1)

在匹配字符串时...您需要将其切片以匹配字符串文字,因为您尝试将String&str匹配。

Ok(val) => match &val[..] { // <---- note the ampersand and the square brackets with two dots inside

此外,一旦执行此操作,编译器可能会抱怨您的"q"模式无法访问。这是因为你的“全能”(下划线)在明确的"q"匹配之前......所以只需重新排序它们。

变成:

match line {
    Ok(val) => match &val[..] {
        // The lines below are re-ordered
        "q" => std::process::exit(0),
        v @ _ => println!("{:?}", v)
    },
    _ => println!("ERROR")
}

答案 2 :(得分:1)

问题是编译器正在尝试将valString匹配,而"q"模式是&str。这会让你感到不匹配。

相反,请写match val.as_str()以使类型匹配。也许将字符串文字转换为String也可以,但这将是一个无用的堆分配。

其次,模式v @ _是多余的。你说的是“匹配任何内容,并将其命名为v”。但是一个简单的v模式可以做同样的事情。