0

With the following code, I tried to return &str of temperature of user input, but in vain. Then, I am trying to return f32, but still struggle...

Q1. The reason I am getting the error at the bottom is because the scope of 'let temp = String::new();' still persists, even I 'shadow' it later by 'let temp = temp.trim().parse::<f32>();' within the loop?

Q2. How can I rewrite the code so that it returns &str?

fn gettemp() -> f32 {
    let temp = String::new();

    loop {
        println!("What is your temperature?");

        io::stdin().read_line(&mut temp).expect("Failed to read the line");

        let temp = temp.trim().parse::<f32>();

        if !temp.is_ok() {
            println!("Not a number!");
        } else {
            break;
        }
    }

    temp
}

Error:

error[E0308]: mismatched types
  --> src/main.rs:70:5
   |
49 | fn gettemp() -> f32 {
   |                 --- expected `f32` because of return type
...
70 |     temp
   |     ^^^^ expected f32, found struct `std::string::String`
   |
   = note: expected type `f32`
              found type `std::string::String`
user2226755
  • 12,494
  • 5
  • 50
  • 73
Julia O
  • 221
  • 2
  • 13
  • 1
    The second question is answered [here](https://stackoverflow.com/q/29428227/1233251). You might want to stick to the first one. – E_net4 Mar 07 '19 at 11:58

3 Answers3

1

Regarding question 1, you can break out of the loop with a value:

fn gettemp() -> f32 {
    let mut temp = String::new();

    loop {
        println!("What is your temperature?");

        io::stdin().read_line(&mut temp).expect("Failed to read the line");

        let temp = temp.trim().parse::<f32>();

        if !temp.is_ok() {
            println!("Not a number!");
        } else {
            break temp.unwrap() // yield value when breaking out of loop
        }
    }
}

This way, the whole loop's value is the thing you passed along with break.

Regarding question 2, I am not sure if you really want to do this, because &str is a borrowed type. I think you want to return an String in this case which owns the data.

phimuemue
  • 34,669
  • 9
  • 84
  • 115
  • Thanks for your comment @phimuemue. What is the difference between 'return' and 'break' with return value? Either makes any difference? – Julia O Mar 10 '19 at 03:12
  • @JuliaO `return` returns from the whole function, while `break` prematurely exits a loop. – phimuemue Mar 10 '19 at 13:43
1

A1 - nope, that's not how shadowing works. Let's look at your code with comments.

fn gettemp() -> f32 {
    let temp = String::new(); // Outer

    loop {
        // There's no inner temp at this point, even in the second
        // loop pass, etc.

        println!("What is your temperature?");

        // Here temp refers to the outer one (outside of the loop)
        io::stdin().read_line(&mut temp).expect("Failed to read the line");

        // Shadowed temp = let's call it inner temp
        let temp = temp.trim().parse::<f32>();
        //    ^      ^
        //    |      |- Outer temp
        //    |- New inner temp

        // temp refers to inner temp
        if !temp.is_ok() {
            println!("Not a number!");
        } else {
            // Inner temp goes out of scope
            break;
        }

        // Inner temp goes out of scope
    }

    // Here temp refers to outer one (String)
    temp
}

A2 - you can't return &str. @E_net4 posted a link to the answer why. However, you can return String. You can do something like this nn case you'd like to have a validated String:

fn gettemp() -> String {
    loop {
        println!("What is your temperature?");

        let mut temp = String::new();
        io::stdin()
            .read_line(&mut temp)
            .expect("Failed to read the line");

        let trimmed = temp.trim();

        match trimmed.parse::<f32>() {
            Ok(_) => return trimmed.to_string(),
            Err(_) => println!("Not a number!"),
        };
    }
}

I see couple of another problems in your code.

let temp = String::new();

Should be let mut temp, because you'd like to borrow mutable reference later (&mut temp in the read_line call).

Another issue is the loop & read_line. read_line appends to the String. Run this code ...

let mut temp = "foo".to_string();
io::stdin().read_line(&mut temp).unwrap();
println!("->{}<-", temp);

... and enter 10 for example. You'll see following output ...

->foo10
<-

... which is not what you want. I'd rewrite gettemp() in this way:

fn gettemp() -> f32 {
    loop {
        println!("What is your temperature?");

        let mut temp = String::new();
        io::stdin()
            .read_line(&mut temp)
            .expect("Failed to read the line");

        match temp.trim().parse() {
            Ok(temp) => return temp,
            Err(_) => println!("Not a number!"),
        };
    }
}

IMHO explicit return temp is much cleaner & readable (compared to suggested break out of the loop with a value).


A3 - Why we don't need to explicitly state <f32> in temp.trim().parse()

It's inferred by the compiler.

fn gettemp() -> f32 { // 1. f32 is return type
    loop {
        println!("What is your temperature?");

        let mut temp = String::new();
        io::stdin()
            .read_line(&mut temp)
            .expect("Failed to read the line");

        match temp.trim().parse() {
        // 4. parse signature is pub fn parse<F>(&self) -> Result<F, ...>
        //    compiler knows it must be Result<f32, ...>
        //    Result<f32, ...> = Result<F, ...> => F = f32
        //    F was inferred and there's no need to explicitly state it
            Ok(temp) => return temp,
            //  |                |
            //  |      2. return type is f32, temp must be f32
            //  |
            //  | 3. temp must be f32, the parse result must be Result<f32, ...>            
            Err(_) => println!("Not a number!"),
        };
    }
}
zrzka
  • 20,249
  • 5
  • 47
  • 73
  • My question is why we don't need to explicitly state in 'temp.trim().parse()' I looked at documentation that states "Parses this string slice into another type." Can you elaborate on this, please? – Julia O Mar 10 '19 at 04:22
  • @JuliaO I updated my answer. Check the _A3 - Why we don't need to explicitly state in temp.trim().parse()_ at the end. – zrzka Mar 10 '19 at 09:23
0

In your program, loop { ... } creates a new scope. The scope of the second temp starts where it's defined and ends when loop ends. See the following example:

fn main() {
    let a = 1;
    {
        let a = 2;
        println!("{}", a);
    }
    println!("{}", a);
}

This prints 2, 1。

If you want to return a string, use (the code is fixed according to the comment below):

fn gettemp() -> String {
    loop {
        let mut temp = String::new();
        println!("What is your temperature?");
        std::io::stdin().read_line(&mut temp).expect("Failed to read the line");
        temp = temp.trim().to_string();
        match temp.parse::<f32>() {
            Err(_) => println!("Not a number!"),
            _ => return temp,
        }
    }
}

&str is a borrowed reference. You cannot return a borrowed reference to a local variable which will be released when the function returns.

Hong Jiang
  • 2,260
  • 1
  • 13
  • 13
  • It's wrong, see my answer. First - `read_line` appends to the `temp` - try to enter `a10`. `temp` will contain `a\n10\n` and this `loop` will never end. Second - you're matching `temp.trim().parse::()` result and then you're returing untrimmed `temp` - try to enter `10` and the return value will be `"10\n"`, not `"10"`, which isn't probably what you want. – zrzka Mar 08 '19 at 09:00
  • @zrzka you're right. I didn't realize read_line appends to the buffer. – Hong Jiang Mar 08 '19 at 10:27