Ghost new line and spaces added to String automatically in Rust - string

I created a empty Mutable String variable using String::new() before a loop start. Then I printed the string value as soon as I entered the loop, changed the type of the same variable to integer u32 by with user input, after trimming all spaces, \n, \r etc..
At the next Iteration of the loop, The value of the variable is back to String and was about to change its type, but when I checked the value of String by printing it, It had some ghost \n and spaces or some ghost characters inherited from the previous integer value.
if the integer is 3 digit, for eg 534 it has 5 characters
if the integer is 1 digit, for eg 3 it has 3 characters
if I give empty value as input, The parsing fails it stays as String, but still in next iteration the String has 2 characters.
I created a function to keep track of the type of variable.
use std::io;
//function to return type of a variable
fn type_of<T>(_: &T) -> String {
return format!("{}", std::any::type_name::<T>());
}
fn main() {
let mut guess = String::new();
loop {
println!(
"At start of loop : {},{}",
type_of(&guess),
guess.chars().count()
);
println!("value : {}", guess);
//emptying string
String::clear(&mut guess);
println!(
"after clearing : {},{}",
type_of(&guess),
guess.chars().count()
);
//getting input for string
println!("Enter value :");
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
//converted its values to interger u32 after trimming spaces,\n and \r and stuffs like that
let guess: u32 = match guess.trim().parse() {
Ok(a) => a,
Err(b) => {
println!("{}", b);
println!("after reciving error : {}", type_of(&guess));
continue;
}
};
println!("after type conversion : {}", type_of(&guess));
println!("value: {}", guess);
}
}
the output was :
At start of loop : alloc::string::String,0
value :
after clearing : alloc::string::String,0
Enter value :
111
after type conversion : u32
value: 111
At start of loop : alloc::string::String,5
value : 111
after clearing : alloc::string::String,0
Enter value :
1
after type conversion : u32
value: 1
At start of loop : alloc::string::String,3
value : 1
after clearing : alloc::string::String,0
Enter value :
cannot parse integer from empty string
after reciving error : alloc::string::String
At start of loop : alloc::string::String,2
value :
after clearing : alloc::string::String,0
Enter value :
What causes this?
Is there a way to maintain the value before the loop, at the start of every iteration?
or may be maintain the value of Integer from previous iteration and u32 Type at the same time?
I ran into this problem when I was trying to learn rust using "The Book" from rust docs, to be specific when I was trying to mess around with the code from Chapter 2 (Guess a number project).

There is a misunderstanding on how variables work in Rust. Different variables with the same name can exist, a process called shadowing. In this program, we have two variables called guess.
The following simplification of the previous code shows this pattern.
let guess: mut = String::new(); // <-- guess #1, lives outside loop
loop {
guess.clear();
println!("Enter value :");
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
// guess #2, lives inside loop
// vvvvv
let guess: u32 = match guess.trim().parse() {
Ok(a) => a,
Err(b) => {
eprintln!("{}", b);
continue;
}
};
println!("value: {}", guess);
}
The first one is always of type String, and the second one is always of type u32. Variables can never change type. What does change is which one can be seen and used in what scope. Since the second guess is only declared in the middle of the loop, mentioning guess before that declaration will mean the first guess, which is the string.
Combining the two facts that:
read_line reads and includes newline characters into the output string;
trim only returns a string slice, without modifying the underlying String value.
then it makes sense that guess will contain trailing newline characters at the beginning of the loop statement after the first iteration.
Is there a way to maintain the value before the loop, at the start of every iteration? or may be maintain the value of Integer from previous iteration and u32 Type at the same time?
With this last question rephrased to mean "a way to maintain the integer value from the previous iteration", then that is possible by giving it a new name and moving it up. In the example below, guess_num is reassigned on each iteration rather than declared each time.
let guess: mut = String::new();
let mut guess_num: u32 = 0;
loop {
println!("Previous number (or 0 if first iteration): {}", guess_num);
guess.clear();
println!("Enter value :");
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
guess_num = match guess.trim().parse() {
Ok(a) => a,
Err(b) => {
eprintln!("{}", b);
continue;
}
};
println!("value: {}", guess);
}
See also:
Why do I need rebinding/shadowing when I can have mutable variable binding?
How to ignore the line break while printing a string read from stdin?
The Rust Programming Language, 3.1 Variables and Mutability

Related

Got the wrong value from the console using match

I have not yet used match so I can't understand it very well. I want to try to write a number in the console, but I got the wrong value from match.
My code:
let mut choice = String::new();
let choice = io::stdin().read_line(&mut choice).unwrap();
match choice {
1 => println!("1"),
2 => println!("2"),
3 => println!("3"),
_ => println!("this number doesn't exist"),
};
And the printed value is 2, but I wrote 1 in the console.
read_line doesn't return your input; it returns the number of bytes entered (including the newline at the end). You entered 1 followed by a newline, so 2 characters, so you get 2 as the return value. The actual text is stored in the string you passed a mutable reference to.
let mut choice = String::new();
// Modifies the choice variable in-place.
io::stdin().read_line(&mut choice).unwrap();
// Now choice contains our input, so let's parse it as an integer.
let choice = i32::from_str(&choice).unwrap();
match choice {
...
}

Access value after it has been borrowed

I have the following function. It is given a file. It should return a random line from the file as a string.
fn get_word(word_list: File) -> String {
let reader = BufReader::new(word_list);
let lines = reader.lines();
let word_count = lines.count();
let y: usize = thread_rng().gen_range(0, word_count - 1);
let element = lines.nth(y);
match element {
Some(x) => println!("Result: {}", x.unwrap()),
None => println!("Error with nth"),
}
let word = String::new(""); // Once the error is gone. I would create the string.
return word;
}
But I keep getting this error:
93 | let lines = reader.lines();
| ----- move occurs because `lines` has type `std::io::Lines<BufReader<File>>`, which does not implement the `Copy` trait
94 | let word_count = lines.count();
| ------- `lines` moved due to this method call
...
99 | let element = lines.nth(y);
| ^^^^^^^^^^^^ value borrowed here after move
|
I am new to Rust and have been learning by try and error. I don't know how to access the data after I have called the count function. If there is another method to accomplish what I want, I would gladly welcome it.
The .count() method consumes the iterator. From the documentation
Consumes the iterator, counting the number of iterations and returning it.
This method will call next repeatedly until None is encountered, returning the number of times it saw Some. Note that next has to be called at least once even if the iterator does not have any elements.
In other words, it reads the file content and discards it. If you want to get the Nth line, then you have to re-read the file using another iterator instance.
If your file is small, you can save the read lines in a vector:
let lines = reader.lines().collect::<Vec<String>>();
Then the length of the vector is the number of lines and you can avoid re-reading the file, but if it's a large file you may end-up crashing with "out of memory" error. In that case you should re-read the file content, or use a better strategy such as indexing where the new lines are, so you can jump straight to the new line, without having to re-read a lot of data.
The value returned by lines is an iterator, which reads the file sequentially. To count the number of lines, the iterator is consumed: self is taken by value; ownership is transferred into the count() function. So you can't rewind and then request the nth line.
The easiest solution is to read all the lines into a vector:
let lines = reader.lines().collect::<Vec<String>>();
let word_count = lines.len();
let y: usize = thread_rng().gen_range(0, word_count - 1);
let word = lines[y].clone();
return word;
Notice the clone call: you can't simply write return lines[y]; because you'd be borrowing the string from the vector, but the vector is destroyed as soon as the function returns. By returning a clone of the string, this is avoided.
(to_owned or even to_string would also work. You can also avoid a copy by using swap_remove; I'm not sure there is a more elegant way to move one element from a vector and discard the rest.)
Note that counting the lines and then selecting one of them requires you to either rewind the iterator and go through it twice (once to count and once to select), or to store everything in memory first (e.g. with .collect::<Vec<_>>). Selecting a random line from the list can however be done in a single pass by randomly choosing on each line whether to keep the currently selected line or replacing it with the latest read line:
fn get_word(word_list: File) -> String {
let reader = BufReader::new(word_list);
let lines = reader.lines();
let mut selected = lines.next().unwrap();
let mut count = 0;
for l in lines {
count += 1;
if thread_rng().gen_range (0, count) == 0 {
selected = l;
}
}
match selected {
Ok(x) => return x,
Err(_) => {
print!("Error get_word");
return String::new();
}
}
}
Or of course the simplest way is to just use choose:
fn get_word(word_list: File) -> String {
use rand::seq::IteratorRandom;
let reader = BufReader::new(word_list);
match reader.lines.choose (thread_rng()) {
Some (Ok (x)) => return x,
_ => {
print!("Error get_word");
return String::new();
}
}
}
In order to solve this problem I used the solution given of using .collect::<Vec<String>> but the whole solution needs a little more work. At least in my case.
First: .lines returns a Iterator of type Result<std::string::String, std::io::Error>.
Second: To access the value of this vector I have to borrow it with &.
Here the working function:
fn get_word(word_list: File) -> String {
let reader = BufReader::new(word_list);
let lines = reader.lines().collect::<Vec<_>>();
let word_count = lines.len();
let y: usize = thread_rng().gen_range(0, word_count - 1);
match &lines[y] {
Ok(x) => return x.to_string(),
Err(_) => {
print!("Error get_word");
return String::new();
}
}
}

Initialize vector through input string using for loop in Rust

let mut input_player = String::new();
let mut player_name = Vec::new();
println!("Enter names of the player: ");
for a in 1..(player_number+1){
println!("Enter names of the player # {}: ", a);
io::stdin()
.read_line(&mut input_player)
.expect("failed to read from stdin");
player_name.push(input_player);
}
I am facing problem while initializing Vector through input string in for loop
There are 2 misconceptions that you have here.
First you are trying to read into the string without clearing it first.
Lets say the first name was Foo and second was Bar.
After the first read_line, value of input_player would be Foo indeed. But the second read_line will not overwrite, it will append. So now the value will be FooBar, which probably is not what you want.
The second and more important is that you gave the string away to the vec in the first loop iteration. But you are trying to use it in second iteration. This comes into the domain of ownership, which you can read more about here.
What you need is a new string for each iteration of the loop. So define a new input_player inside the loop.

Why does a truncated string Rust print as an empty pair of parenthesis?

I have
use std::io;
fn main() {
println!("CHAR COUNT");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect(
"Failed to read line",
);
let string_length = guess.len() - 2;
let correct_string_length = guess.truncate(string_length);
println!("Your text: {}", guess);
println!("Your texts wrong length is: {}", string_length);
println!("Your texts correct length: {}", correct_string_length);
}
The last line gives me
error[E0277]: the trait bound `(): std::fmt::Display` is not satisfied
--> src/main.rs:15:47
|
15 | println!("Your texts correct length: {}", correct_string_length);
| ^^^^^^^^^^^^^^^^^^^^^ `()` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
|
= help: the trait `std::fmt::Display` is not implemented for `()`
= note: required by `std::fmt::Display::fmt`
What am I doing wrong? If I use {:?} then I get () instead of a formatted string.
When in doubt, go to the docs - here's the function signature of String::truncate:
fn truncate(&mut self, new_len: usize)
Note two things:
It takes self as &mut.
It has no return value.
From that, the problem becomes pretty clear - truncate does not return a new truncated string, it truncates the existing string in place.
This might seem a little unintuitive at first, but Rust APIs tend not to allocate new objects in memory unless you specifically ask them to - if you're never going to use guess again, then it'd be ineffecient to create a whole new String. If you wanted to make a truncated copy, then you'd need to be explicit:
let truncated = guess.clone();
truncated.truncate(string_length);
Or if you just wanted to reference part of the existing string, you could do what Ryan's answer suggests.
Just to compliment the other answers here..
Attempting to truncate a string in Rust that is not on a character boundary will cause a runtime panic.
So while this works now:
let correct_string_length = &guess[..string_length];
If you're trying to truncate a string with wider characters, your code will panic at runtime. This is especially true if you're truncating user input.. who knows what it could be. For example:
fn main() {
let s = "Hello, 世界";
println!("{}", &s[..8]); // <--- panic
}
You can use the str::is_char_boundary(usize) method to make sure you're not about to break up a wide character accidentally:
fn print_safely(s: &str, mut idx: usize) {
loop {
if s.is_char_boundary(idx) || idx >= s.len() - 1 {
break;
}
idx += 1;
}
println!("{}", &s[..idx]);
}
User input could be anything so this is just something to consider.
Playground link: http://play.integer32.com/?gist=632ff6c81c56f9ba52e0837ff25939bc&version=stable
truncate operates in place, which is why it returns (). Looks like you’re just looking for a regular non-mutating substring:
let correct_string_length = &guess[..string_length];

Trying to borrow variable binding from outside of loop

I'm rewriting an average calculator from my Java textbook in Rust. It uses a loop to ask for several inputs, but I think I've run into an issue with ownership (as there's a let grade inside the loop), so I'm trying to get a better understanding.
When I declare grade outside the loop, only the first input value is recorded: every subsequent grade value becomes 0, regardless of input. If I declare grade inside the loop, every input value is recorded properly.
Is there a way to use borrowing that will let me declare grade outside the loop, and still have this little program work?
use std::io;
fn main() {
let mut total: u32 = 0;
let mut count: u32 = 0;
let mut grade = String::new(); //This one
while count < 5 {
println!("Enter number:");
io::stdin().read_line(&mut grade)
.expect("failed.");
let grade: u32 = match grade.trim().parse() {
Ok(num) => num,
Err(_) => 0,
};
total = total + grade;
count = count + 1;
}
println!("Tot: {}\nAvg: {}", total, total as f64/count as f64);
}
read_line appends to the string you're reading into, so if you input 1 and then 2, grade will contain 1\n2 or something to that effect. This can't be parsed as an integer so the match below will always return 0. The solution is to empty the string before reading: grade.clear();

Resources