Making a simple game with Rust
Rust is a systems programming language that offers memory safety, concurrency, and performance. In this blog post, we’ll explore some Rust basics while building a simple “guess the number” game. This will give us a hands-on introduction to key Rust concepts and syntax.
この記事の目次
Setting Up the Project
Let’s start by creating a new Rust project. Open your terminal and run:
cargo new guess_the_number cd guess_the_number
This creates a new Rust project named “guess_the_number” and changes into the project directory. Cargo, Rust’s package manager and build system, sets up a basic project structure for us.
Understanding the Project Structure
Before we dive into coding, let’s take a moment to understand the project structure Cargo has created for us:
1. Cargo.toml
: This is the manifest file for our Rust package. It contains metadata about the package and its dependencies.
2. src/main.rs
: This is the entry point of our program. It contains the main()
function, which is where our program starts executing.
Let’s open src/main.rs
in your preferred text editor. You’ll see a basic “Hello, world!” program:
fn main() { println!("Hello, world!"); }
We’ll be replacing this with our guess the number game shortly.
Basic Rust Concepts
Before we start building our game, let’s cover some basic Rust concepts:
1. Variables: In Rust, variables are immutable by default. To make them mutable, we use the mut
keyword.
2. Functions: Functions are defined using the fn
keyword, followed by the function name and parameters in parentheses.
3. Macros: Macros in Rust are denoted by an exclamation mark (!
). println!
is a macro that prints text to the console.
4. Types: Rust is a statically typed language, but it can often infer types. When needed, we declare types using a colon (:
) after the variable name.
5. Modules: Rust organizes code into modules. The standard library is available through the std
module.
Now, let’s start building our game!
Building the Guess the Number Game
We’ll create a game where the computer generates a random number, and the player tries to guess it. Let’s update our main.rs
file:
use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Welcome to 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(_) => 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; } } } }
Now, let’s break down this code and explain the Rust concepts it introduces:
Importing Modules
use std::io; use std::cmp::Ordering; use rand::Rng;
Here, we’re importing necessary modules. std::io
for input/output operations, std::cmp::Ordering
for comparing numbers, and rand::Rng
for generating random numbers.
Note: To use the rand
crate, we need to add it to our Cargo.toml
file:
[dependencies] rand = "0.8.5"
Generating a Random Number
let secret_number = rand::thread_rng().gen_range(1..=100);
This line generates a random number between 1 and 100 (inclusive). We’re using the rand
crate’s thread_rng()
function to get a random number generator, and gen_range()
to generate a number within the specified range.
The Game Loop
loop { // Game logic here }
We use a loop
to create an infinite loop, which will continue until we explicitly break out of it (when the player guesses correctly).
Getting User Input
let mut guess = String::new(); io::stdin() .read_line(&mut guess) .expect("Failed to read line");
We create a mutable String
to store the user’s input. Then we use io::stdin().read_line()
to read a line of input from the user. The &mut guess
argument tells the method to store the input in our guess
variable. The expect
method is used for error handling – if reading the line fails, it will panic with the given message.
Parsing the Guess
let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, };
This block introduces several important Rust concepts:
1. Shadowing: We’re declaring a new variable named guess
, which shadows the previous guess
variable. This is often used to convert a value from one type to another.
2. Result handling: The parse()
method returns a Result
type, which is either Ok
or Err
. We use a match
expression to handle both cases.
3. Type annotation: We specify that guess
should be a u32
(unsigned 32-bit integer).
4. Error handling: If parsing fails (i.e., the user didn’t enter a valid number), we continue
the loop, asking for another guess.
Comparing the Guess
match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => { println!("You win!"); break; } }
Here, we use another match
expression to compare the guess with the secret number. The cmp
method returns an Ordering
, which can be Less
, Greater
, or Equal
. We handle each case, printing an appropriate message. If the guess is correct, we print “You win!” and break
out of the loop, ending the game.
Conclusion
This simple game demonstrates several key Rust concepts:
1. Variables and mutability
2. Input and output
3. Random number generation
4. Loops and control flow
5. Match expressions
6. Error handling
7. Type conversion
By building this game, we’ve gotten a taste of Rust’s syntax and some of its core features.
As you keep learning Rust, you’ll come across more advanced concepts like ownership, borrowing, and lifetimes. They are what make Rust unique, but are out of scope for this blog. Hope you had fun reading this!
カテゴリー: