I've been following the rust book, and I made a Celsius to Fahrenheit converter as it advised me. After a bit, I came up with this code; however in my opinion, there's a lot of unnecessary parts that I couldn't figure out how to simplify. Here is the code:

fn readnum() -> i32 {
    loop {
      let mut raw_inp = String::new();
      match std::io::stdin().read_line(&mut raw_inp) {
         Err(er) => {println!("\nError! {}",er); continue; }
         Ok(_o) => {} 
      match raw_inp.trim().parse() {
         Err(er) => {println!("\nError! {er}"); continue;}
         Ok(o) => { return o}

fn main() {
   loop {
      println!("Enter your celsius number: ");
      let val:i32 = readnum();
      let converted:f32 = (val as f32 * (9f32/5f32 ))+ 32f32;
      println!("In Fahrenheit it is: {}", converted);
  • \$\begingroup\$ What do you think are the "unnecessary parts"? \$\endgroup\$ Commented Apr 17 at 19:29
  • \$\begingroup\$ @MaartenBodewes The first match in the readnum function especially as the Ok just does nothing. \$\endgroup\$
    – icy icy
    Commented Apr 17 at 19:39

1 Answer 1


I don't think there is too much to simplify. Note that we generally would err towards readable code rather than concise code. Or at least that was what I was used to as Java coder. Actually I'd introduce a function or two.

You've indicated that the error handling may contain "unnecessary parts". I'd indicate that you could visit this page and then look at how unwrap_or_else can be used.

Personally though I'd probably have one function asking for a number, returning an error if the function doesn't. Then you can loop until you do get a number. This would work well with the propagating errors & ? operator.

Some remarks:

  • Asymmetry: the user enters an i32 which is then converted to an f32 (32 bit floating point); both are similar units and should probably have similar representations (f32 makes the most sense to me, as temperature is a continuous spectrum).
  • readnum retrieves a number from a user (with possible retries), but the name only indicates that a number is read from somewhere.
  • Generally it is a good idea use the input source as a parameter, e.g. for testing (i.e. the call would be inputNumber(std::io::stdin()), uh or something similar (Rust seems to have this return something of type Stdin which seems to be it's own class).
  • You have one println!("\nError! {}",er) and one println!("\nError! {er}" which looks unbalanced. I'd personally prefer the latter. I'd also indicate the type of error.
  • There really should be a function called celciusToFahrenheit in there. Always make sure that the "business logic" is separated from the I/O and therefore the main() function. Remember you want to test your functions for any serious piece of code, e.g. using Unit tests.
  • The println!("Enter your celsius number: "); is in the main function while it is part of the UI code. It makes most sense to have the println and read_line statements grouped together in one function - possibly with sub-functions where required.
  • It's best practice to also repeat the println!("Enter your celsius number: "); so that the user knows what to do after getting an error.


  • The code is well indented. There are some spacing issues with a few enters too many at the end of functions:
            // no reason for an empty line here
            // nor here
  • In general I don't have too much problems with names of identifiers either, the code is readable.

As an extreme nitpick, I'd generally first handle the positive outcome in a match statement (i.e. focus on the good flow). There is something to be said for fail-fast, but basically the matches are at the same level so I don't think it would count as an argument.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.