I set a goal for myself to learn Rust this year. I've got a fair bit of experience in python, but I'm not a coder by trade, so it's mostly self-taught. I use it for data collection and analysis primarily.
I'm reading Steve Klabnik's book "The Rust Programming Language (2e)", and just finished the 3rd chapter which has three exercises: Convert temperatures between C and F, generate the nth Fibonacci number, and print the lyrics to the worst Christmas Carol ever conceived.
I did it all in one project, coding each exercise as its own function. I intentionally only used information presented in the book to this point, so I'm certain there are better ways to do a lot of this. I'd appreciate feedback on style, things to look out for, and maybe previews of those "better ways" to have in mind as I continue in the book.
My main() function just gives a simple menu to select which exercise to run -- it was a convenient way for me to work through each of the three problems without having to either make different projects for each or run through each completed problem before testing the one I'm working on.
use std::io;
fn main() {
// Set up a simple menu to choose which exercise code to run. This helps to not have to run
// every single exercise every time I do cargo run.
loop {
println!("Chapter 3 Exercises");
println!("-------------------");
println!("1. Exercise 1");
println!("2. Exercise 2");
println!("3. Exercise 3");
println!("9. Quit");
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("failed to read line");
let mode: usize = match input.trim().parse() {
Ok(num) => num,
Err(_) => continue, // if anything weird happens, just ignore it and start over.
};
if mode == 1 {
// Run the first exercise
exercise1();
} else if mode==2 {
// Run the second exercise
exercise2();
} else if mode==3 {
// Run the third exercise
exercise3();
} else if mode==9 {
// Quit the program
break;
} else {
// Throw a fit if anything else happens, and start over.
println!("Invalid option {mode}");
continue;
}
}
}
Exercise 1
fn exercise1() {
println!("Exercise 1");
println!("----------");
// select the conversion mode
let mut input = String::new();
println!("1. C->F");
println!("2. F->C");
io::stdin()
.read_line(&mut input)
.expect("failed to read line");
let mode: usize = match input.trim().parse() {
Ok(num) => num,
Err(_) => 999999, // if non-numerical data is entered, set to a value that
// triggers a return to the main menu.
};
if mode == 1 {
// Convert Celsius to Fahrenheit
// Get Celsius Value
let mut input = String::new();
println!("Enter the number of degrees in Celsisus.");
io::stdin()
.read_line(&mut input)
.expect("failed to read line");
let c: f32 = match input.trim().parse() {
Ok(num) => num,
Err(_) => return, // If non-numerical data is given, just return out
// of the function and go back to the menu.
};
// Convert to Fahrenheit
let f: f32 = c*9.0/5.0+32.0;
println!("{c} deg C is {f} deg F.");
} else if mode == 2 {
// Convert Fahrenheit to Celsius
// Get Fahrenheit Value
let mut input = String::new();
println!("Enter the number of degrees in Fahrenheit.");
io::stdin()
.read_line(&mut input)
.expect("failed to read line");
let f: f32 = match input.trim().parse() {
Ok(num) => num,
Err(_) => return, // If non-numerical data is given, just return out
// of the function and go back to the menu.
};
// Convert to Celsius
let c: f32 = (f-32.0)*5.0/9.0;
println!("{f} deg F is {c} deg C.");
} else {
// Any numerical value other than 1 or 2 is ignored, and the user
// is sent back to the main menu.
println!("Invalid input; returning to main menu.");
return;
}
println!("Exercise 1 concluded.")
}
Exercise 2
fn exercise2() {
println!("Exercise 2");
println!("----------");
// Have the user provide which Fibonnaci number to calculate. Note that for i64, the
// 93rd Fibonacci number is the largest that will not trigger an overflow.
let mut input = String::new();
println!("Enter the nth Fibonnaci number to calcluate (max 93).");
io::stdin()
.read_line(&mut input)
.expect("failed to read line");
let n: isize = match input.trim().parse() {
Ok(num) => num,
Err(_) => -1,
};
// We need to track the previous two values to calculate the next.
let mut value: u64 = 1; // holds the most recent value
let mut last_value: u64 = 0; // holds the previous value
let mut old_value: u64; // temporary placeholder to handle changing to the next number
// Step through the Fibonnaci sequence n times.
for _i in 1..n {
old_value = value; // temporarily hold a copy of the current value
value += last_value; // new value is the current plus the previous
last_value = old_value; // set the previous to what the current value was before
}
println!("The {n}th Fibonnaci number is {value}");
println!("Exercise 2 concluded.");
}
Exercise 3
fn exercise3() {
println!("Exercise 3");
println!("----------");
// Set up arrays for the ordinal day and the gifts given
let days: [&str; 12] = ["first", "second", "third", "fourth", "fifth", "sixth",
"seventh", "eigth", "ninth", "tenth", "eleventh", "twelfth"];
let gifts: [&str; 12] = ["a partridge in a pear tree!",
"two turtle doves,",
"three french hens,",
"four calling birds,",
"five golden rings,",
"six geese a-laying,",
"seven swans a-swimming,",
"eight maids a-milking,",
"nine ladies dancing,",
"ten lords a-leaping,",
"eleven pipers piping,",
"twelve drummers drumming,"];
// Step through the 12 verses
for i in 0..12 {
// Each verse starts with this line:
println!("On the {} day of Christmas, my true love gave to me:", days[i]);
// If it's the first verse, we have to do something special, since there is no 'and' in
// this verse.
if i==0 {
println!(" {}", gifts[0]);
} else {
// for other verses, we need to step through each of the gifts in reverse order.
for j in (0..i+1).rev() {
// at j==0, we're back to the first gift, which gets the 'and' added
if j==0 {
println!(" and {}", gifts[j]);
} else {
println!(" {}", gifts[j]);
}
}
}
}
println!("Exercise 3 concluded.");
}
Any thoughts, improvements, things to watch out for are welcome!