Skip to content

Instantly share code, notes, and snippets.

@Najaf
Created June 4, 2016 20:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Najaf/a1e08fe7236976de465e6b295d843cc1 to your computer and use it in GitHub Desktop.
Save Najaf/a1e08fe7236976de465e6b295d843cc1 to your computer and use it in GitHub Desktop.
use std::io;
use std::io::Write;
use std::str::FromStr;
fn prompt<F>(question: &str) -> Result<F, F::Err>
where F: FromStr
{
let mut answer = String::new();
print!("{} ", question);
let _ = io::stdout().flush();
let _ = io::stdin().read_line(&mut answer);
answer.trim().to_string().parse::<F>()
}
fn main() {
let length_in_feet: f64 = prompt("Length of room in feet?").unwrap();
let width_in_feet: f64 = prompt("Width of room in feet?").unwrap();
let area_in_square_feet = length_in_feet * width_in_feet;
let conversion_factor = 0.09290304;
let area_in_square_metres = area_in_square_feet * conversion_factor;
println!("You entered dimensions of {} feet by {} feet.",
length_in_feet,
width_in_feet);
println!("The area is");
println!("{} square feet", area_in_square_feet);
println!("{} square metres", area_in_square_metres);
}
@skade
Copy link

skade commented Jun 5, 2016

About your question on Twitter: https://twitter.com/alinajaf/status/739197792795668480

First of all, you could even leave off the paramatrisation in line 12:

 answer.trim().to_string().parse()

The reasoning can be done step by step, backwards:

  • length_in_feet is f64
  • thus, unwrap must return f64
  • unwrap is a method of Result<F, F::Err> and returns (by signature) F, so F must be f64 in that case
  • f64 satisfies F: FromStr, so that is okay, and F::Err can also be deduced from that, because we can now look up the exact implementation of the FromStr trait for f64
  • On the call site at 12, we already know that F is f64, so we need the parse implementation for f64

This will lead prompt to be compiled in a specialised fashion, in the resulting binary, there will be a prompt_f64 implementation that will be used there, there is no guessing at runtime.

Finally, you have a subtle coercion bug:

 answer.trim().to_string().parse::<F>()

can be written as:

 answer.trim().parse()

(What happens here is you allocate a String, but parse works on the immutable string slice &str. But String can be automatically seen as &str due to some coercion rules, so it works transparently, but you don't need the intermediated String)

@Najaf
Copy link
Author

Najaf commented Jun 5, 2016

@skade thanks, this answer is brilliant, I really appreciate you taking the time to write it.

First of all, you could even leave off the paramatrisation in line 12

Is this because of the combination of:

  • The return type of prompt if Result<F, F::Err> where F: FromStr and the compiler has enough info at this stage to know that F is f64 AND
  • answer.trim().to_string().parse() would be the last expression in the function, so that compiler knows that it must return an F

?

This will lead prompt to be compiled in a specialised fashion, in the resulting binary, there will be a prompt_f64 implementation that will be used there, there is no guessing at runtime.

Ahah! Interestingly the reason I wrote this was because I wrote a prompt_f64 function myself and thought there has to be a way to make a "generic" prompt function (i.e. one where I don't need to manually write separate functions for each type I want returned).

(What happens here is you allocate a String, but parse works on the immutable string slice &str. But String can be automatically seen as &str due to some coercion rules, so it works transparently, but you don't need the intermediated String)

Thanks, that's great to know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment