A simple, intuitive Rust library for reading typed user input from stdin. Stop fighting with read_line() and parsing - just ask for what you need.
stdin-helper provides straightforward functions for getting typed input from the user. If you've ever been frustrated writing the same read_line() boilerplate over and over, this library is for you.
Every function accepts a repeat flag: when set to true, it automatically retries on invalid input. Ask for an integer and the user types "hello"? It will ask again until it gets a valid number. When set to false, it returns a Result immediately, giving you full control over error handling.
Add to your Cargo.toml:
[dependencies]
stdin-helper = "2.0.1"Or use cargo:
cargo add stdin-helperAll functions return InputResult<T>, which is an alias for Result<T, InputError<...>>. Use repeat: true to loop until valid input is received, or repeat: false to get a Result back immediately.
use stdin_helper::{get_string, get_i32, get_f64, get_bool};
fn main() {
// repeat: true — keeps asking until the user enters something valid
let name = get_string("What's your name? ", true).unwrap();
let age = get_i32("How old are you? ", true).unwrap();
let height = get_f64("Height in meters: ", true).unwrap();
let verified = get_bool("Are you human? ", true, &["yes", "y", "true"], &["no", "n", "false"]).unwrap();
println!("{} is {} years old and {:.2}m tall", name, age, height);
}When using repeat: false, you can propagate or handle errors idiomatically:
use stdin_helper::get_i32;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let age = get_i32("How old are you? ", false)?;
println!("You are {age} years old.");
Ok(())
}stdin-helper distinguishes between two kinds of failures via InputError<E>:
InputError::Io(io::Error)— something went wrong reading from stdin (e.g. stdin was closed).InputError::Parse(E)— the user's input couldn't be converted to the requested type.
This distinction matters because I/O errors are generally unrecoverable, while parse errors are expected and safe to retry. When repeat: true is used, parse errors are silently retried; I/O errors are still returned immediately regardless of the flag.
use stdin_helper::{get_i32, InputError};
match get_i32("Enter a number: ", false) {
Ok(n) => println!("Got: {n}"),
Err(InputError::Io(e)) => eprintln!("I/O error: {e}"),
Err(InputError::Parse(e)) => eprintln!("Invalid input: {e}"),
}| Function | Returns | Description |
|---|---|---|
get_string(prompt, repeat) |
InputResult<String> |
Gets text input (any text is valid) |
get_char(prompt, repeat) |
InputResult<char> |
Gets a single character |
| Function | Returns | Range |
|---|---|---|
get_i8(prompt, repeat) |
InputResult<i8> |
-128 to 127 |
get_i16(prompt, repeat) |
InputResult<i16> |
-32,768 to 32,767 |
get_i32(prompt, repeat) |
InputResult<i32> |
-2,147,483,648 to 2,147,483,647 |
get_i64(prompt, repeat) |
InputResult<i64> |
-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
get_i128(prompt, repeat) |
InputResult<i128> |
-(2^127) to 2^127 - 1 |
get_isize(prompt, repeat) |
InputResult<isize> |
Platform-dependent (32 or 64 bits) |
| Function | Returns | Range |
|---|---|---|
get_u8(prompt, repeat) |
InputResult<u8> |
0 to 255 |
get_u16(prompt, repeat) |
InputResult<u16> |
0 to 65,535 |
get_u32(prompt, repeat) |
InputResult<u32> |
0 to 4,294,967,295 |
get_u64(prompt, repeat) |
InputResult<u64> |
0 to 18,446,744,073,709,551,615 |
get_u128(prompt, repeat) |
InputResult<u128> |
0 to 2^128 - 1 |
get_usize(prompt, repeat) |
InputResult<usize> |
Platform-dependent (32 or 64 bits) |
| Function | Returns | Description |
|---|---|---|
get_f32(prompt, repeat) |
InputResult<f32> |
32-bit floating point |
get_f64(prompt, repeat) |
InputResult<f64> |
64-bit floating point (recommended) |
Boolean input requires you to explicitly define which strings count as true and which count as false. Comparison is case-insensitive after .trim().
// Signature
pub fn get_bool(
msg: &str,
repeat: bool,
true_values: &[&str],
false_values: &[&str],
) -> Result<bool, InputError<BoolParseError>>let confirmed = get_bool(
"Confirm? ",
true,
&["yes", "y", "sim", "s", "true", "1"],
&["no", "n", "nao", "false", "0"],
).unwrap();If you need a type not listed above, use the core generic function directly. Any type that implements FromStr works:
use stdin_helper::get_input;
let value: u64 = get_input("Enter a value: ", true).unwrap();For numeric types specifically, get_number<T> is also available with a Numeric trait bound that prevents accidental misuse with non-numeric types:
use stdin_helper::get_number;
let value: f32 = get_number("Enter a value: ", true).unwrap();use stdin_helper::{get_i32, get_string};
fn main() {
let x = get_i32("First number: ", true).unwrap();
let op = get_string("Operator (+, -, *, /): ", true).unwrap();
let y = get_i32("Second number: ", true).unwrap();
let result = match op.as_str() {
"+" => x + y,
"-" => x - y,
"*" => x * y,
"/" => x / y,
_ => {
println!("Invalid operator");
return;
}
};
println!("{} {} {} = {}", x, op, y, result);
}use stdin_helper::{get_f64, get_string};
fn main() {
let temp = get_f64("Enter temperature: ", true).unwrap();
let unit = get_string("Unit (C/F): ", true).unwrap().to_uppercase();
match unit.as_str() {
"C" => {
let fahrenheit = (temp * 9.0 / 5.0) + 32.0;
println!("{}°C = {:.1}°F", temp, fahrenheit);
}
"F" => {
let celsius = (temp - 32.0) * 5.0 / 9.0;
println!("{}°F = {:.1}°C", temp, celsius);
}
_ => println!("Invalid unit. Use C or F."),
}
}use stdin_helper::{get_f32, get_i32};
fn main() {
let num_grades = get_i32("How many grades? ", true).unwrap();
let mut sum = 0.0;
for i in 1..=num_grades {
let grade = get_f32(&format!("Grade {}: ", i), true).unwrap();
sum += grade;
}
let average = sum / num_grades as f32;
println!("Average: {:.2}", average);
}use stdin_helper::get_i32;
fn read_positive() -> Result<i32, Box<dyn std::error::Error>> {
// repeat: false gives us the Result immediately so we can propagate
let n = get_i32("Enter a number: ", false)?;
Ok(n)
}Each function displays your prompt and waits for a line of input. What happens next depends on the repeat flag:
repeat: true— if the input can't be parsed into the requested type, the prompt is displayed again. This continues until valid input is received or an I/O error occurs.repeat: false— tries once and returns aResult. A parse failure becomesErr(InputError::Parse(...)), which you can handle or propagate with?.
I/O errors (InputError::Io) always surface immediately regardless of the repeat flag, since they signal a problem with stdin itself rather than with the user's input.
In upcoming versions, stdin-helper will support a builder pattern API for more advanced input validation scenarios:
// THIS DOES NOT WORK YET - Coming in a future version!
use stdin_helper::InputBuilder;
let user_rating: u32 = InputBuilder::new("Please input a rate: ")
.range(1..=10, "Number must be between 1 and 10")
.on_error("That doesn't look like a number")
.get()?;This will allow you to:
- Define custom validation rules
- Provide specific error messages for different validation failures
- Set retry limits
- Add custom validators with closures
If you'd like to contribute to making this happen, check out the repository!
# Build the library
cargo build
# Run tests
cargo test
# Generate documentation
cargo doc --openFor beginners: Eliminates boilerplate so you can focus on learning Rust concepts rather than input handling mechanics. Use repeat: true and .unwrap() to get started fast.
For prototyping: Quickly test ideas without writing parsing code every time.
For simple CLIs: Perfect for straightforward command-line tools where complex error handling isn't needed.
For teaching: Great for educational projects and coding exercises where input handling shouldn't be the focus.
This library is designed for simplicity, not for complex input scenarios. Consider alternatives if you need:
- Non-blocking input handling
- Input with timeout limits
- Production-grade validation with custom retry limits or closures (coming in a future version)
For those cases, using std::io directly or other crates might be better choices.
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contributions are welcome! Whether it's:
- Bug reports and feature requests
- Documentation improvements
- Code contributions
- Examples and use cases
Feel free to open an issue or submit a pull request.
Created by Richard Dias Alves
Inspired by the frustration of writing read_line() boilerplate repeatedly. Built to make Rust's stdin interaction as simple as it should be.
Repository: https://github.com/richarddalves/stdin-helper
Crate: https://crates.io/crates/stdin-helper