diff --git a/0000-scan.md b/0000-scan.md new file mode 100644 index 00000000000..10e363366b5 --- /dev/null +++ b/0000-scan.md @@ -0,0 +1,96 @@ +- Start Date: +- RFC PR #: +- Rust Issue #: + +# Summary + +Add `scan!`, `scanln!`, etc. macros which mirror `print!`, `println!`, etc. for +doing straightforward input from stdin, a file, or other Reader. + +# Motivation + +There is no easy way in Rust to read data from files or stdin. The current +solution is to get a `Reader`, read some bytes and call the appropriate methods +to parse them into data. Whilst fine as a basis for large applications, for +tutorials or small applications (two use cases are university assignments and +programming competitions such as Google CodeJam) this is too clunky and long- +winded. We need something like `scanf` or C++ streaming io to allow easy reading +of data into Rust data structures. + +# Detailed design + +I propose adding four user facing macros: `scan!`, `scanln!`, `read!`, and +`readln!`. The `ln` variations expect input terminated by a newline and are +otherwise the same as the other variations. `read!` takes an additional +parameter - a `Reader` to use as a source, `scan!` uses stdin. From now on I'll +describe only `scanln!`, the other macros are identical other than the source +and terminating new line. + +The syntax of `scanln!` mirrors, and is a syntactic subset of `println!`. It +would take a string literal and a number of expressions. The string literal +would be similar to a format string, but uses only a subset of the mini- +language. The expressions must all evaluate to lvalues. + +The semantics of the macro is that text in the 'format' string must be matched +in the input, and holes in the format string cause the corresponding lvalue in +the argument list to be filled with the parsed value. + +Some examples: + +``` +let mut x = 0; +let mut y = 0; +scanln!("{} {}", x, y); +``` + +Would parse "3 4" from stdin so that `x` is 3 and `y` is 4. + + +``` +let mut name = StrBuf::new(); +let mut age = 0; +scanln!("{}: {}", name, age); +``` + +Would parse "Bob: 36" from stdin so that `name` is Bob and `age` is 36. + +Any data structure which implements `FromStr` could be used as a receiving +argument. This would preserve modularity. + +Various features of the format string mini-language could be used. For example, +specifying a width would mean reading only those characters. Named arguments +would be supported. Using formatting information would guide parsing, for +example, `{:8x}` would read eight characters and attempt to parse them as +hexadecimal into an integer. + +There's a fair bit of detail to work out about exactly what subset to use - it +is not clear to me if specifying padding is useful, or how (or if) to deal with +methods for internationalisation, etc. + +Note that a format string such as `{}{}` would have to be disallowed since it +could not be unambiguously parsed. We must require some string literal between +non-fixed sized holes. + +# Drawbacks + +Might encourage people to use this simple UI for tasks its not suited to, such +as a full lexer/parser or general input. + +# Alternatives + +An unsafe scanf is not really an option for Rust. + +We could do something more like C++'s streaming io, but that doesn't pair very +well with `println!` etc. + +We could have something like `scan!` but using an API totally different from +`print!`. + +Do nothing. + +# Unresolved questions + +What exactly should the mini-language look like? + +How to handle failure? Should we have two versions of each macro, one which +returns a result and one which just fails? Or is just the former?