Home > other >  Ghost new line and spaces added to String automatically in Rust
Ghost new line and spaces added to String automatically in Rust

Time:02-22

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).

CodePudding user response:

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:

  1. read_line reads and includes newline characters into the output string;
  2. 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:

  • Related