【问题标题】:Idiomatic way of mimicking Python's input function in Rust在 Rust 中模仿 Python 输入函数的惯用方式
【发布时间】:2020-05-23 05:31:31
【问题描述】:

我有两个三个不同版本的函数,它们模仿 python 中的 input 函数。

use std::io::{self, BufRead, BufReader, Write};

// Adapted from https://docs.rs/python-input/0.8.0/src/python_input/lib.rs.html#13-23
fn input_1(prompt: &str) -> io::Result<String> {
    print!("{}", prompt);
    io::stdout().flush()?;
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer)?;
    Ok(buffer.trim_end().to_string())
}

// https://www.reddit.com/r/rust/comments/6qn3y0/store_user_inputs_in_rust/
fn input_2(prompt: &str) -> io::Result<String> {
    print!("{}", prompt);
    io::stdout().flush()?;
    BufReader::new(io::stdin())
        .lines()
        .next()
        .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Cannot read stdin"))
        .and_then(|inner| inner)
}

// tranzystorek user on Discord (edited for future reference)
fn input_3(prompt: &str) -> io::Result<String> {
    print!("{}", prompt);
    std::io::stdout().flush()?;
    BufReader::new(std::io::stdin().lock())
        .lines()
        .take(1)
        .collect()
}

fn main() {
    let name = input_1("What's your name? ").unwrap();
    println!("Hello, {}!", name);
    let name = input_2("What's your name? ").unwrap();
    println!("Hello, {}!", name);
    let name = input_3("What's your name? ").unwrap();
    println!("Hello, {}!", name);
}

但它们似乎是非常不同的方法,我不知道使用其中一种方法是否有任何优势。根据我的阅读,拥有像 python 的 input 这样的函数并不像看起来那么简单,这就是标准库中没有的原因。

使用上面编写的任何版本可能会遇到什么问题?还有另一种更惯用的方式来编写这个input 函数吗? (2018年版)

另外,在这里:How can I read a single line from stdin? 一些答案使用lock() 方法,但我不明白它的目的。

我正在学习来自 python 的 Rust。

【问题讨论】:

    标签: rust


    【解决方案1】:

    这主要是风格问题 - 两种方法都可以接受。我认识的大多数 Rustaceans 可能会喜欢第二种方法,因为它在风格上更实用,但在这种情况下真的没关系。

    我要做的关键更改是在您的第二个示例中使用 lock 方法。

    要了解锁定方法,请考虑以下场景:如果您的应用程序是多线程的,并且两个线程同时尝试从 stdin 读取,会发生什么情况?

    锁确保一次只有一个线程可以访问stdin。您总是通过锁访问stdin。事实上,如果您查看Stdin::read_line 的实现——您在第一个示例中调用的方法,您会发现它是一个非常简单的单行代码:

    self.lock().read_line(buf)
    

    因此,即使您没有明确调用 lock,它仍然在幕后使用。

    其次.next()在这种情况下不会返回None,因为它会阻塞直到输入数据,所以你可以在这里安全地使用.unwrap()而不是.ok_or/.and_then

    最后你错过了input_1 中的.trim_end() ;)。

    fn input_2(prompt: &str) -> io::Result<String> {
        print!("{}", prompt);
        io::stdout().flush()?;
        io::stdin()
            .lock()
            .lines()
            .next()
            .unwrap()
            .map(|x| x.trim_end().to_owned())
    }
    

    【讨论】:

    • 编译器抱怨它无法在next()? 上将错误转换为std::io::Error。另外,使用 BufReader 有什么好处吗?
    • 双重unwrap() 解决了这个问题。安全吗?
    • 抱歉我的代码完全被破坏了 - 很晚了,昨晚我很累 - 我把结果弄乱了。我已经编辑了我的答案以更正它。双重展开是不安全的,因为结果可能是错误的。相反,您使用 map 来更改 Result 的“ok”值。
    猜你喜欢
    • 2014-02-21
    • 1970-01-01
    • 2010-12-08
    • 2018-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-24
    相关资源
    最近更新 更多