Is there appropriate macro for simplified console input?


#1

I’m new to the Rust. After reading the manual i can not find a convinient way for simplified console input.
For me it is quite natural for someone after cosy write!() and writeln!() to expect something like read!() readln!()). Of course i’m quite aware of general problems with input operations, and can use readers with bunch of unnecesary verbosity along… but this way looks rather inadecvate for many tasks (for example -educational ones).


#2

I’m not sure what you expect from read!(). You can always give your students some helper functions if you think there should be simpler ones.

use std::io::BufRead;

macro_rules! read_line(
    () => {{
        let stdin = std::io::stdin();
        let stdin = stdin.lock();
        let mut lines = stdin.lines();
        lines.next().unwrap()
    }}
);

fn main() {
    let first_line = read_line!();
}

For educational tasks maybe the following would be prettier, especially considering that iterators are everywhere in rust, and should not be skipped in any tutorial.

use std::io::stdin;
use std::io::BufRead;

fn main() {
    let stdin = stdin();
    for line in stdin.lock().lines() {
        let line = line.unwrap();
        // line is a String
        // do something with line
    }
}

#3

Thank you for the answer but it’s not near simple as Pascal readln(a,b,c) or even C scanf("%f %d %c",&a,&b,&c) and C++ stream working.
I just try to pose the problem more explicitly…
Let us look upon a standard educational tasks logical design:

InputData()
DoSomething()
OutputResult()

The main accent is upon “DoSomething()” part. So It is not good (actually it is VERY BAD) when InputData() both conceptually and in the usage looks more difficult and noisy then main part. moreover- in the rust auxiliary parts (input and output) looks very very asymmetric. That is also not good.
To prove abovementioned - just look upon the Rust manual ----you will see a bit less then nothing about user input.
For me it looks like a dirty game…
About “homemade helpers” for the problem - for me it is not good solution,at all - there must be adecvate way treat this case “from the box”


#4

Well… It depends on what you want. If you want to read entire structs from any input, you should be using the serde or rustc-serialize crates. They offer single function calls that take an iterator and return an object. But they require a formatted input.

If you just want to read space separated strings, integers and floats then I assume you want something along the lines of the following code, just without all the hazzle?

use std::io::BufRead;
use std::str::FromStr;

fn main() {
    let stdin = std::io::stdin();
    let stdin = stdin.lock();
    let mut chunks = stdin.split(b' ');
    let str = String::from_utf8(chunks.next().unwrap().unwrap()).unwrap();
    let num = i32::from_str(&String::from_utf8(chunks.next().unwrap().unwrap()).unwrap());
}

all the unwrap is messy, I agree. Without it I find it much better than the C/C++/Java/whatnot variants


#5

[quote=“dizer, post:3, topic:1000”]
About “homemade helpers” for the problem - for me it is not good solution,at all - there must be adecvate way treat this case “from the box”
[/quote]It was a strategic decision to make std::io pretty minimal for 1.0 and not put every convenience possible there just yet, the situation is likely to get better in the future. Taking into account that Rust is pretty young so far, it’s a fair suggestion to roll you own input handling crate that fits your teaching needs.


#6

you catch the thought - i like to see dedicated crate but in the standart bundle. yet concerning your example it looks ugly to me and i bet… it will look horribly in the eyes of newbies


#7

2gkos - OK that is the adecvate answer. thank you. I do hope that the Rust developers&designers put some efforts into the topic… so the dirty game turns opened…


#8

So I’ve started working on some super simple functions. See it on github
Documentation and cargo coming soon.

bascially you can do simple things like

let s: String = si::read_line();
let i: i32 = si::read(); // read until a whitespace and try to get a number
let word: String = si::read(); // read until a whitespace (but not including it)

#9

yet what do you think about

let (i,f)=si::read!("{} {}");
let (i,f):(i32, f64)=si::readln!("{} {}");

#10

and this works now:

let tup: (i32, i8, String) = read!("{}, {}, {}");
assert_eq!(tup, (42, 99, "Ä".to_string()));

#11

the simple io functions are now hosted on crates.io: https://crates.io/crates/text_io


#12

The text_io crate has been updated to version 0.0.4 and is now partially compatible with the beta. Calling read to read multiple values (into a tuple) does not work in the beta version, as there is no way to detect the return tuple arity from inside a macro.


#13

The text_io crate has been updated with a better documentation and some minor bugfixing. I’m working on a scan! macro to allow multiple values to be read on stable in one expression, by passing the variables as arguments to the macro.


#14

@oli_obk: Big Thanks! (Dankeschön :slight_smile:)

„text_io“ is very useful – especially for beginners – and fills the gap between Rust and C: Why doesn’t Rust have something like „scan!“ or other simple input-macros in standard-library?!
Any plans or official proposals to merge text_io with std::io???


#15

@p53: I suspect part of the reason is that input is intrinsically much more tricky to handle than output, due to the possibility of accidentally or voluntarily ill-formed input. Output is always right, input is not, and Rust’s input interfaces were built to make users aware of that fact right from the start.

Many other programming languages have made the mistake of being excessively trusting of user inputs by default in the past. And many concrete bugs and vulnerabilities resulted from that, ranging from trying to shove infinite input into a finite buffer (scanf), to arbitrary code execution (pretty much every form of language-level serialization to date).

For any code with security or reliability implications, Rust’s way of stressing that input is just a bunch of bytes and that you need to carefully check any other fact that you think you know about it before relying on that property to be true is a nice change.

(Which is not to say, of course, that a special-purpose “beginner interface” cannot be built for teaching purposes. That is what text_io does. But you need to keep in mind why the Rust input interfaces are the way they are, and will have to introduce the “real” IO interfaces to your students sooner or later, so it’s a trade-off that must be handled carefully.)