Reading an integer from input and assigning it to a variable - input

I've been trying to find an easy way to read variables in Rust, but haven't had any luck so far. All the examples in the Rust Book deal with strings AFAIK, I couldn't find anything concerning integers or floats that would work.

I don't have a Rust compiler on this machine, but based in part on this answer that comes close, you want something like...
let user_val = match input_string.parse::<i32>() {
Ok(x) => x,
Err(_) => -1,
};
Or, as pointed out in the comments,
let user_val = input_string.parse::<i32>().unwrap_or(-1);
...though your choice in integer size and default value might obviously be different, and you don't always need that type qualifier (::<i32>) for parse() where the type can be inferred from the assignment.

To read user input, you always read a set of bytes. Sometimes, you can interpret those bytes as a UTF-8 string. You can then further interpret the string as an integral or floating point number (or lots of other things, like an IP address).
Here's a complete example of reading a single line of input and parsing it as a 32-bit signed integer:
use std::io;
fn main() {
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Not a valid string");
let input_num: i32 = input.trim().parse().expect("Not a valid number");
println!("Your number plus one is {}", input_num + 1);
}
Note that no user-friendly error handling is taking place. The program simply panics if reading input or parsing fails. Running the program produces:
$ ./input
41
Your number plus one is 42

A set of bytes comprises an input. In Rust, you accept the input as a UTF-8 String. Then you parse the string to an integer or floating point number. In simple ways you accept the string and parse it, then write an expect`` statement for both, to display a message to the user what went wrong when the program panics during runtime.
fn main() {
let mut x = String::new();
std::io::stdin().read_line(&mut x)
.expect("Failed to read input.");
let x: u32 = x.trim().parse()
.expect("Enter a number not a string.");
println!("{:?}", x);
}
If the program fails to parse the input string then it panics and displays an error message. Notice that the program still panics and we are not handling an error perfectly. One more thing to notice is that we can use the same variable name x and not some x_int because of the variable shadowing feature. To handle the error better we can use the match construct.
fn main() {
let mut x = String::new();
match std::io::stdin().read_line(&mut x) {
Ok(_) => println!("String has been taken in."),
Err(_) => {
println!("Failed to read input.");
return;
},
};
let x: u32 = match x.trim().parse() {
Ok(n) => {
println!("Converted string to int.");
n
},
Err(_) => {
println!("Failed to parse.");
return;
},
};
println!("{:?}", x);
}
This is longer way but a nicer way to handle errors and input and parse a number.

Related

Why does .flat_map() with .chars() not work with std::io::Lines, but does with a vector of Strings?

I am trying to iterate over characters in stdin. The Read.chars() method achieves this goal, but is unstable. The obvious alternative is to use Read.lines() with a flat_map to convert it to a character iterator.
This seems like it should work, but doesn't, resulting in borrowed value does not live long enough errors.
use std::io::BufRead;
fn main() {
let stdin = std::io::stdin();
let mut lines = stdin.lock().lines();
let mut chars = lines.flat_map(|x| x.unwrap().chars());
}
This is mentioned in Read file character-by-character in Rust, but it does't really explain why.
What I am particularly confused about is how this differs from the example in the documentation for flat_map, which uses flat_map to apply .chars() to a vector of strings. I don't really see how that should be any different. The main difference I see is that my code needs to call unwrap() as well, but changing the last line to the following does not work either:
let mut chars = lines.map(|x| x.unwrap());
let mut chars = chars.flat_map(|x| x.chars());
It fails on the second line, so the issue doesn't appear to be the unwrap.
Why does this last line not work, when the very similar line in the documentation doesn't? Is there any way to get this to work?
Start by figuring out what the type of the closure's variable is:
let mut chars = lines.flat_map(|x| {
let () = x;
x.unwrap().chars()
});
This shows it's a Result<String, io::Error>. After unwrapping it, it will be a String.
Next, look at str::chars:
fn chars(&self) -> Chars
And the definition of Chars:
pub struct Chars<'a> {
// some fields omitted
}
From that, we can tell that calling chars on a string returns an iterator that has a reference to the string.
Whenever we have a reference, we know that the reference cannot outlive the thing that it is borrowed from. In this case, x.unwrap() is the owner. The next thing to check is where that ownership ends. In this case, the closure owns the String, so at the end of the closure, the value is dropped and any references are invalidated.
Except the code tried to return a Chars that still referred to the string. Oops. Thanks to Rust, the code didn't segfault!
The difference with the example that works is all in the ownership. In that case, the strings are owned by a vector outside of the loop and they do not get dropped before the iterator is consumed. Thus there are no lifetime issues.
What this code really wants is an into_chars method on String. That iterator could take ownership of the value and return characters.
Not the maximum efficiency, but a good start:
struct IntoChars {
s: String,
offset: usize,
}
impl IntoChars {
fn new(s: String) -> Self {
IntoChars { s: s, offset: 0 }
}
}
impl Iterator for IntoChars {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
let remaining = &self.s[self.offset..];
match remaining.chars().next() {
Some(c) => {
self.offset += c.len_utf8();
Some(c)
}
None => None,
}
}
}
use std::io::BufRead;
fn main() {
let stdin = std::io::stdin();
let lines = stdin.lock().lines();
let chars = lines.flat_map(|x| IntoChars::new(x.unwrap()));
for c in chars {
println!("{}", c);
}
}
See also:
How can I store a Chars iterator in the same struct as the String it is iterating on?
Is there an owned version of String::chars?

Unable to open a File where the filename is read from standard input [duplicate]

I'm trying to get user input and check if the user put in "y" or "n". Surprisingly, in the below code, neither the if nor the if else case executes! Apparently, correct_name is neither "y" nor "n". How can that be? Am I doing my string conversion wrong or something?
use std::io;
fn main() {
let mut correct_name = String::new();
io::stdin().read_line(&mut correct_name).expect("Failed to read line");
if correct_name == "y" {
println!("matched y!");
} else if correct_name == "n" {
println!("matched n!");
}
}
read_line includes the terminating newline in the returned string.
To remove it, use trim_end or even better, just trim:
use std::io;
fn main() {
let mut correct_name = String::new();
io::stdin()
.read_line(&mut correct_name)
.expect("Failed to read line");
let correct_name = correct_name.trim();
if correct_name == "y" {
println!("matched y!");
} else if correct_name == "n" {
println!("matched n!");
}
}
This last case handles lots of types of whitespace:
Returns a string slice with leading and trailing whitespace removed.
‘Whitespace’ is defined according to the terms of the Unicode Derived Core Property White_Space.
Windows / Linux / macOS shouldn't matter.
You could also use the trimmed result's length to truncate the original String, but in this case you should only use trim_end!
let trimmed_len = correct_name.trim_end().len();
correct_name.truncate(trimmed_len);
read_line includes the terminating newline in the returned string. Add .trim_right_matches("\r\n") to your definition of correct_name to remove the terminating newline.
You can use the chomp-nl crate which provides a chomp function which returns a string slice without the newline characters.
There is also a trait ChompInPlace if you prefer to do this in-place.
Disclaimer: I am the author of this library.

Simplification possible in example using print! and flush?

I started programming Rust a couple of days ago by working through the official documentation. Now I'm trying to challenge my understanding of Rust by working through the book "Exercises for Programmers" by Brian P. Hogan (The Pragmatic Programmers).
The first exercise is to write a program that asks the user for a name and prints out a greeting using that name. Input, string concatenation and output should be done in three distinct steps.
What is your name? Patrick
Hello, Patrick, nice to meet you.
The name will be entered at the same line as the initial prompt. Here's my solution:
use std::io;
use std::io::Write;
fn main() {
print!("What is your name? ");
match io::stdout().flush() {
Ok(_) => print!(""),
Err(error) => println!("{}", error),
}
let mut name = String::new();
match io::stdin().read_line(&mut name) {
Ok(_) => {
name = name.trim().to_string();
if name.len() > 0 {
let greeting = "Hello, ".to_string() + &name + &", nice to meet you!".to_string();
println!("{}", greeting);
} else {
println!("No name entered, goodbye.");
}
}
Err(error) => println!("{}", error),
}
}
The print! macro doesn't actually output the prompt until I call flush. flush needs error handling, so I need both to handle the Ok and the Err case. In case of Ok, there's nothing useful to do, so I just print! an empty string.
Is there a shorter way to handle this? Maybe the error handling can be skipped or simplified somehow, or the whole print!/flush approach is the wrong one. (Everything works fine, but I could write this shorter in C, after all...)
As other people have said, make sure to read the error handling chapter.
In most cases, you don't want to use println! to report errors. Either you should return the error from your function and let the caller deal with it, or you should use panic! to abort that thread and potentially the process.
match io::stdout().flush() {
Ok(_) => print!(""),
Err(error) => println!("{}", error),
}
Instead of printing nothing (which is inefficient), you can just... do nothing:
match io::stdout().flush() {
Ok(_) => (),
Err(error) => println!("{}", error),
}
Since you don't care about the success case, you can use an if let:
if let Err(error) = io::stdout().flush() {
println!("{}", error);
}
Replacing the println with a panic! would be even better:
if let Err(error) = io::stdout().flush() {
panic!("{}", error);
}
This is almost exactly what Option::unwrap does (source), except it also returns the successful value when present:
pub fn unwrap(self) -> T {
match self {
Some(val) => val,
None => panic!("called `Option::unwrap()` on a `None` value"),
}
}
However, it's even better to use Option::expect which allows you to specify an additional error message:
io::stdout().flush().expect("Unable to flush stdout");
Applying that twice:
use std::io::{self, Write};
fn main() {
print!("What is your name? ");
io::stdout().flush().expect("Unable to flush stdout");
let mut name = String::new();
io::stdin()
.read_line(&mut name)
.expect("Unable to read the line");
let name = name.trim();
if !name.is_empty() {
println!("Hello, {}, nice to meet you!", name);
} else {
println!("No name entered, goodbye.");
}
}
Note that there's no need to re-allocate a String, you can just shadow name, and there's no need to use format just to print out stuff.
Since Rust 1.26.0, you could also choose to return a Result from main:
use std::io::{self, Write};
fn main() -> Result<(), io::Error> {
print!("What is your name? ");
io::stdout().flush()?;
let mut name = String::new();
io::stdin().read_line(&mut name)?;
let name = name.trim();
if !name.is_empty() {
println!("Hello, {}, nice to meet you!", name);
} else {
println!("No name entered, goodbye.");
}
Ok(())
}
but I could write this shorter in C, after all...
I would encourage / challenge you to attempt this. Note that every memory allocation in this program is checked, as is every failure case dealing with the standard output. Many people are not aware that C's printf returns an error code that you should be checking. Try outputting to a pipe that has been closed for an example.

Read input in pattern match branch

My program should read some parameters from the command line. If someone does not provide an optional password parameter the program should ask for it. Therefore the corresponding field is modeled as an Option type.
This works well if the parameter is provided from the command line ( option having the value of Some("..."), but the branch matching the None case does not ask for input.
The simplified program looks like
fn main() {
use std::io::{self,Read};
let arg : Option<String> = None; // Does not terminate
//let arg : Option<String> = Some("Some arg".to_string()); // works well printing 'Some arg'
println!("Checking for password");
let password = match arg {
Some(val) => val.to_string(),
None => {
print!("Password:");
let mut buffer = String::new();
io::stdin().read_to_string(&mut buffer).unwrap();
buffer
}
};
println!("password is {}", password);
}
Running the program preinitialized with Some("Some arg") prints the string "password is Some arg" to the console as expected, but switching to the None does nothing, even not terminating the program.
Could you spot my mistake or give me some advice? I am using rustc verison rustc 1.4.0 (8ab8581f6 2015-10-27). Thanks in advance.
You need to use read_line():
fn main() {
use std::io::{self,Read};
let arg : Option<String> = None; // Does not terminate
//let arg : Option<String> = Some("Some arg".to_string()); // works well printing 'Some arg'
println!("Checking for password");
let password = match arg {
Some(val) => val.to_string(),
None => {
print!("Password:");
let mut buffer = String::new();
io::stdin().read_line(&mut buffer).unwrap();
buffer
}
};
println!("password is {}", password);
}
The read_to_string() function reads until end of file.
Your program does indeed read input, but you need to send it the EOF character (Ctrl-D on Linux) for it to continue executing.

Is it valid to rebind a variable in a while loop?

Is it valid to rebind a mutable variable in a while loop? I am having trouble getting the following trivial parser code to work. My intention is to replace the newslice binding with a progressively shorter slice as I copy characters out of the front of the array.
/// Test if a char is an ASCII digit
fn is_digit(c:u8) -> bool {
match c {
30|31|32|33|34|35|36|37|38|39 => true,
_ => false
}
}
/// Parse an integer from the front of an ascii string,
/// and return it along with the remainder of the string
fn parse_int(s:&[u8]) -> (u32, &[u8]) {
use std::str;
assert!(s.len()>0);
let mut newslice = s; // bytecopy of the fat pointer?
let mut n:Vec<u8> = vec![];
// Pull the leading digits into a separate array
while newslice.len()>0 && is_digit(newslice[0])
{
n.push(newslice[0]);
newslice = newslice.slice(1,newslice.len()-1);
//newslice = newslice[1..];
}
match from_str::<u32>(str::from_utf8(newslice).unwrap()) {
Some(i) => (i,newslice),
None => panic!("Could not convert string to int. Corrupted pgm file?"),
}
}
fn main(){
let s:&[u8] = b"12345";
assert!(s.len()==5);
let (i,newslice) = parse_int(s);
assert!(i==12345);
println!("length of returned slice: {}",newslice.len());
assert!(newslice.len()==0);
}
parse_int is failing to return a slice that is smaller than the one I passed in:
length of returned slice: 5
task '<main>' panicked at 'assertion failed: newslice.len() == 0', <anon>:37
playpen: application terminated with error code 101
Run this code in the rust playpen
As Chris Morgan mentioned, your call to slice passes the wrong value for the end parameter. newslice.slice_from(1) yields the correct slice.
is_digit tests for the wrong byte values. You meant to write 0x30, etc. instead of 30.
You call str::from_utf8 on the wrong value. You meant to call it on n.as_slice() rather than newslice.
Rebinding variables like that is perfectly fine. The general rule is simple: if the compiler doesn’t complain, it’s OK.
It’s a very simple error that you’ve made: your slice end point is incorrect.
slice produces the interval [start, end)—a half-open range, not closed. Therefore when you wish to just remove the first character, you should be writing newslice.slice(1, newslice.len()), not newslice.slice(1, newslice.len() - 1). You could also write newslice.slice_from(1).