Reading a character from stdin

I'm making a simple program that requires a charater input (without hitting enter, just a character), however std::io::stdin().read_line() takes a whole line as input.

I've seen a stackoverflow post about this, but needs external crates which seems like an overkill for a simple program. Is this possible without crates, or do I have to use them?

I use the console crate for this, it has a read_char method.

If you don't want to use the crate, you could check how they implement it.

That's not as easy as it looks, due to the Unicode.

You can easily read a single byte (or a fixed amount of bytes) from an stdin, via its Read implementation. However, to get a character, you have to:

  • read one byte;
  • decide, depending on it, how many bytes you want to read to complete the character;
  • read these bytes;
  • convert the result to char (probably with intermediate conversion to i32).
2 Likes

Note that this method does not use std::io::stdin, but rather reads from the corresponding file descriptor directly (at least in Unix).

1 Like

how will I do this? I tried read, read_exact, and take, but they all need the user to hit enter

On most Unices, stdin will be line buffered whenever it is connected to a console. That means the characters entered by the user will only be flushed to your program when the enter key is pressed.

You will need to use an OS-specific API to make stdin unbuffered.

And console does exactly that, in this case - by calling libc::cfmakeraw on the corresponding file descriptor.

2 Likes

Ah, I was expecting something like Console.ReadKey() in C# but for rust

On UNIX .NET switches the terminal to non-canonical mode on every Console.ReadKey() call and switches it back once the function returns. Rust does not do terminal magic like this in the std library.

2 Likes

There's fundamental difference between C# standard library and Rust standard library.

C# includes in it's standard library lots of things which may be nice for the developers to have.

Rust tries to include only things which have to be in the standard library, lack of which may fragment Rust community (imagine standard library without String) or which are not possible to implement in a stable Rust (like Fn/FnMut/FnOnce traits).

Everything else belongs to the “third-party” crates.

P.S. It would have been really nice for novices if there would have been a nice set of crates which can, together, be considered a replacement for C#/Java/Python python SDK. Set of well-developed crates which solve common needs and are considered “good enough” today. There would be no need to keep everything in that list supported “forever” (like the stuff in standard library) but at least it would be a place where you can go and find out that crate Foo is no longer supported and Bar is it's incompatible replacement. Like “RLS is obsolete, try rust-analyzer instead” (something which makes quite a few total newbies make the wrong choice).

2 Likes

FWIW, this set of crates does exist. It's just that they aren't formally listed anywhere or endorsed, and the set will change over time. Often you just need to go to crates.io, type a keyword into search, and look for the most popular result.

  • csv
  • regex
  • serde - general serialization framework
  • serde_json - JSON
  • reqwest - HTTP client
  • log
  • rand
  • zip
  • ring - crypto
  • base64
  • byteorder - utility for handling little/big endian numbers and byte streams
  • url
  • anyhow - error handling in top-level applications where you care more about things like backtraces and what caused what
  • thiserror - error handling in libraries when you want to define your own fine-grained custom error types
  • clap and/or structopt - for command-line argument parsing
  • itertools

and so on...

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.