Feature Request: `scan!` macro

Recently a friend of mine who mostly codes i Java and javascript asked me to teach him Rust. To practice we'd make some simple programs like a number guessing game. But I wanted to introduce the language incrementally, without introducing a concept without explaining it before and telling them to just not worry about it, we'll talk about it later...

However, getting input from stdin involves many concepts from the language. If I were to write an expression to read a value from stdin, i'd probably write

{
    let mut buf = String::new();
    std::io::stdin().read_line(&mut buf).unwrap();
    buf.parse::<T>().unwrap()
}

This has a block expression, a mutable variable, the associated function String::new, modules and paths, mutable references, the Result enum, and a generic function.

While making the small number guessing game, I had to write this into a function and just say "you'll understand it later", it terrified them.

reading from stdin is a common thing to want to do, especially if you're playing around with a language to see how it works. I think a scan! macro that does that would be a great addition that would help minimize some of the learning curve and remove stigma around rust

a scan! macro feels as natural as println!

as to how it works, i dont have a design. probably something that crashes on io errors and evaluates to the result of the str::parse call?

let input: Result<i32, <i32 as FromStr>::Error> = scan!();
let input: Result<i32, _> = scan!();
let input = scan!(i32);
1 Like

Suggestions to add full scanf-like machinery have not been very favorably received in the past, but there absolutely should at the very least be a simple, beginner-friendly way to read single numbers from input. This is a real problem in how teachable and approachable Rust is and, as an aside, has also been one of the biggest issues in using Java as a teaching language.

Rust is a very command-line-oriented language, there's no way to e.g. conjure a dialog to ask the user for input, so at the very least there should be a simple, robust way to read numeric data from stdin.

3 Likes

As someone who wrote a crate for doing approximately this a long time ago, a few quick thoughts.

First, what you describe should be scanln!, since it eats an entire line of input at a time. You can't avoid this, because FromStr has no ability to consume only part of its input. Also, given what it expands to, perhaps parseln! would be better.

And that will be pretty much limited to single values on a line, or a full line as a string, which means anyone trying to port even very simple sscanf code is going to immediately run into problems.

I feel like if you want something simple, a read_line or readln function/macro that just gives you a line of text makes more sense. Yes, you still have to parse the value from that, but at least the operation of it is clear.

I also don't feel like a "simple" scan! macro is really possible. When I worked on mine, I immediately got stuck trying to figure out how to handle all the various ways of splitting up the input, handling groups and repetitions, different representations, etc.[1] And these were all concerns I had to address to make something that felt meaningfully more useful than just doing read_line().split().parse() (or the nearest compilable equivalent).

If there was a single, clear "winner" among the third party crates, you could consider including that. But insofar as I'm aware, there isn't one.

Still, I do agree that improvement in this area would be a good thing.


  1. It doesn't help that I got sucked into trying to make it look like macro_rules!/match, since it felt like it made sense to keep the syntax and capabilities vaguely compatible. ↩︎

6 Likes

Maybe scan is a bad name. I think we just need a simpler way to get input, all the machinery for doing scanf-like "formatted scanning" seems like total overengineering for what should just be read stdin and parse to a type

If the developer wants to do formatted parsing of that input, he can do it himself, it doesn't need to be builtin into the macro

would input! be a better name?

Previous conversation:

7 Likes

The rust code is not more difficult than

public class Main {
  public static void main(){
    Scanner s = new Scanner(System.in);
    int i = s.nextInt();
    // ...
  }
}
1 Like

cc @PO8, who is also interested in working on inputln.

Sorry I missed this earlier. I don't think any scan!() will be a part of the Input RFC, but a separate RFC would make sense. There's a lot of interesting semantics to resolve, which if done wrong is part of what makes C scanf() so terrible. In particular, handling of whitespace and "noise characters" becomes problematic.

This little snippet of crufty, undocumented code is what I used in the past for this kind of thing with AdventOfCode libaoc/reparse/reparse.rs at main · BartMassey/libaoc · GitHub . I like a regex-based solution more than some arbitrary semantics, but it definitely isn't easier to use.

I don't think a input function/macro should also concern itself with parsing... why would we expect that?

Why not just a simple way to get what has been inputted in stdin? then let the user do the necessary parsing

Why would we expect a print macro to also concern itself with formatting?

1 Like

Why would we expect a print macro to also concern itself with formatting?

I agree that the symmetry is charming, but formatting and parsing are two VERY different processes.

1 Like