Building a Guessing Game
Now that we’ve covered the basics, let’s build a simple guessing game to get a better feel for Rust. We’ll build the same application in both Rust and JavaScript to highlight the differences and similarities.
Project Home
Section titled “Project Home”We’ll create a number guessing game where:
- The program generates a random number between 1 and 100
- The player inputs guesses
- The program provides feedback on each guess
- The game continues until the player guesses correctly
Setting Up the Rust Project
Section titled “Setting Up the Rust Project”Let’s start by creating a new Rust project:
cargo new guessing_game
cd guessing_game
The Initial Rust Program
Section titled “The Initial Rust Program”Open src/main.rs
and replace its contents with:
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess:");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
JavaScript Equivalent
Section titled “JavaScript Equivalent”For comparison, here’s how we’d start in JavaScript:
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
console.log("Guess the number!");
console.log("Please input your guess:");
readline.question('', (guess) => {
console.log(`You guessed: ${guess}`);
readline.close();
});
Understanding the Rust Code
Section titled “Understanding the Rust Code”Let’s break down the Rust code:
use std::io;
- Imports the I/O library (similar torequire
in Node.js)let mut guess = String::new();
- Creates a mutable variable (notemut
)io::stdin().read_line(&mut guess)
- Gets user input and stores it inguess
.expect("Failed to read line")
- Handles potential errors (we’ll explore better error handling later)
Key differences from JavaScript:
- Variables are immutable by default in Rust; we need
mut
to make them mutable - In Rust, we handle errors explicitly with
.expect()
or other error handling methods
Adding Random Number Generation
Section titled “Adding Random Number Generation”Let’s update our program to generate a random number. First, we need to add the rand
crate to our dependencies.
Edit Cargo.toml
:
[dependencies]
rand = "0.8.5"
Now, update src/main.rs
:
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..=100);
println!("The secret number is: {}", secret_number); // For debugging
println!("Please input your guess:");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = guess.trim().parse().expect("Please type a number!");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
JavaScript Equivalent
Section titled “JavaScript Equivalent”const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
console.log("Guess the number!");
const secretNumber = Math.floor(Math.random() * 100) + 1;
console.log(`The secret number is: ${secretNumber}`); // For debugging
console.log("Please input your guess:");
readline.question('', (guessInput) => {
const guess = Number(guessInput.trim());
console.log(`You guessed: ${guess}`);
if (guess < secretNumber) {
console.log("Too small!");
} else if (guess > secretNumber) {
console.log("Too big!");
} else {
console.log("You win!");
}
readline.close();
});
Key Differences So Far
Section titled “Key Differences So Far”-
Error Handling:
- Rust: Explicit with
.expect()
or other methods - JavaScript: Often implicit or with try/catch blocks
- Rust: Explicit with
-
Type Conversion:
- Rust: Explicit parsing with
.parse()
to convert strings to numbers - JavaScript: Implicit conversion or explicit with
Number()
- Rust: Explicit parsing with
-
Comparison:
- Rust: Uses pattern matching with
match
expression - JavaScript: Uses if/else statements
- Rust: Uses pattern matching with
Adding a Loop for Multiple Guesses
Section titled “Adding a Loop for Multiple Guesses”Let’s update our Rust program to allow multiple guesses:
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..=100);
loop {
println!("Please input your guess:");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please type a number!");
continue;
}
};
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
JavaScript Equivalent
Section titled “JavaScript Equivalent”const readline = require('readline');
function guessTheNumber() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
console.log("Guess the number!");
const secretNumber = Math.floor(Math.random() * 100) + 1;
const askGuess = () => {
rl.question('Please input your guess: ', (guessInput) => {
const guess = Number(guessInput.trim());
if (isNaN(guess)) {
console.log("Please type a number!");
askGuess();
return;
}
console.log(`You guessed: ${guess}`);
if (guess < secretNumber) {
console.log("Too small!");
askGuess();
} else if (guess > secretNumber) {
console.log("Too big!");
askGuess();
} else {
console.log("You win!");
rl.close();
}
});
};
askGuess();
}
guessTheNumber();
Improved Error Handling
Section titled “Improved Error Handling”Notice how our Rust code now uses pattern matching to handle potential errors when parsing the input:
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please type a number!");
continue;
}
};
This is using Rust’s Result
type, which we’ll explore more in the error handling section.
Running the Complete Game
Section titled “Running the Complete Game”Run the Rust version:
cargo run
Comparing the Full Implementations
Section titled “Comparing the Full Implementations”Let’s highlight the key differences between our Rust and JavaScript implementations:
-
Memory Safety:
- Rust guarantees memory safety at compile time
- JavaScript relies on runtime checks and garbage collection
-
Error Handling:
- Rust uses Result types that force us to handle errors explicitly
- JavaScript typically uses try/catch blocks or callbacks for error handling
-
Type System:
- Rust is statically typed, requiring explicit type annotations
- JavaScript is dynamically typed with optional type systems like TypeScript
-
Concurrency Model:
- Rust’s ownership system prevents data races at compile time
- JavaScript uses an event loop with a single thread by default
-
Performance:
- Rust compiles to native code with performance similar to C/C++
- JavaScript depends on the runtime’s JIT compiler
Next Steps
Section titled “Next Steps”You’ve now successfully built a complete, interactive guessing game using Rust’s language features. Next, we’ll cover Common Programming Concepts in more detail, starting with variables and mutability.