Rust for Beginners: Build a Word Game · lab 01 of 7

[ free ] [ variables · functions · your first test ] [ ~15 min if you have programmed · ~45 if you have not ]
01

Put your game's name on the screen

[ preview ] The lesson is free to read — building it needs a free account.

You'll learn: what a variable is, what a function is, and the small ritual of proving your code works with a test. Those three things are the floor every later lab stands on — and by the end of this one, your terminal will print your game's opening screen.

What we are building

When the finished game starts, the first thing a player sees is the banner:

== lantern ==
Guess the 5-letter word. You have 6 tries.

Two lines. The first carries the game's name. The second states the rules. This lab builds both — and everything you need to build them is on this page.

A variable is a name for a value

In Rust you give a value a name with let:

let word_len = 5;

Read it aloud: let word_len be 5. From that line on, writing word_len means 5. The name is yours to choose; Rust's habit is lowercase words joined with underscores, like word_len and max_guesses.

One rule to meet now rather than mid-lab: a Rust variable does not change unless you say so. After let word_len = 5;, trying to set word_len to 6 is an error — not a warning, an error. If you want a value you can change, you say so up front with mut (short for mutable — changeable):

let mut screen = String::new();

Now screen may change, and the compiler holds you to it: mut values may change, plain let values may not. That is strict, and it is also a gift: every plain let is a promise that the value is still what it was made as.

A function is a small machine

A function takes values in, does one job, and hands a value back. Here is the shape, using the very function you will write:

pub fn rules_line(word_len: usize, max_guesses: usize) -> String {
    // the body goes here
}

Read it piece by piece, because every part means one thing:

  • fnfunction. The keyword that starts every function.
  • pubpublic. Other files may use this function. Without it, the function is private to its own file. main.rs — the small file that actually runs the game — reaches into this file from outside, so this course marks its functions pub.
  • rules_line — the function's name, chosen by us, same naming habit as variables.
  • (word_len: usize, max_guesses: usize) — the values it takes in, called arguments. Each one is a name, a colon, and a type — the kind of value it is. usize is Rust's type for a whole number that counts things: a length, a number of tries. Rust asks you to write the types where values go in and come out, so the compiler can check every call against them.
  • -> String — the arrow says what comes back. This function returns a String: text.
  • { … } — the body: the lines that do the work.

One more Rust habit to meet here, because it looks like magic until it is explained: the last line of a body, without a semicolon, is the value the function returns. A body that ends in screen hands back whatever screen holds.

Text: String and format!

Rust's everyday type for text you build and own is String. Two ways to make one, and one way to grow it — this lab uses all three:

let max_guesses = 6;
let empty = String::new();                    // "" — nothing yet, ready to grow
let line = format!("You have {max_guesses} tries.");  // built from a template

format! is a template machine: inside the quotes, a name in curly braces — {max_guesses} — is replaced by that variable's value. It is how you turn the number 6 into the text "You have 6 tries." without gluing pieces by hand.

The third way is growing a String piece by piece. A String made with String::new() starts empty; push_str adds text to its end:

let mut screen = String::new();
screen.push_str("== ");

That screen.push_str(…) shape — a value, a dot, an action — is called calling a method: push_str is something every String knows how to do. And because pushing changes screen, it only works on a let mut — which is the one place in this lab that needs mut.

Constants: values that are part of the design

Some values are not variables at all — they are decisions. This game's word length is five. Its guess count is six. Rust spells a decision with const:

pub const WORD_LEN: usize = 5;

A const has a name in capitals, always has its type written, and never changes. Your starter file ships three of them — the game's name, the word length, the guess count — already written, so you can see the shape before you write your own functions beneath them.

One crate, many files: pub mod

Your game's code lives in game/lantern/src/, one file per lab. But Rust does not scan folders: a file is not part of your program until a pub mod line names it. The file src/lib.rs is the table of contents, and

pub mod banner;

in it means: there is a file called banner.rs beside me; it is a part of this crate; other code may use it. (The word module means one named box of code — for now, one file.) Until that line exists, the compiler never opens banner.rs at all — you could write anything in there and no error would appear, which is worth knowing before it confuses you.

There is a second spelling, pub mod banner { … }, which holds the module's code right there between the braces instead of in its own file. You will meet that form at the bottom of this very lab — the test block is a module written inline. Same idea both times: a named box of code. One names a file; the other holds its contents directly.

The test ritual

A test is a function that calls your function and checks the answer. That is the entire idea. Rust has it built in: mark a function with #[test], and cargo test will find it, run it, and print a line saying whether it passed.

The checking is done by two built-in tools:

  • assert_eq!(left, right)these two values must be equal. If they are, nothing happens and the test passes. If not, the test fails and both values are printed, so you can see exactly how they differ.
  • assert!(condition)this must be true. Used when there is no pair to compare, only a yes-or-no question.

Tests live at the bottom of the file they test, inside a block the project has you type out. Here is what each of its lines means:

  • #[cfg(test)]compile the next item only when testing. Your finished game does not carry its tests around; this line is what leaves them out of the real build.
  • mod tests { … } — the inline module form from the section above: a named box holding the tests.
  • use super::*; — the first line inside, every time. super means the file around this box, and the * means everything in it. A module is a fresh scope — without this line, the tests cannot see rules_line or GAME_NAME at all, and the compiler will say it cannot find them.

When you run cargo test, every test prints its own line — the test's name, three dots, ok or FAILED. Step 5 of the project shows you the four lines your own run prints.

When Rust says no

You are going to see error messages this lab — on purpose. So here is the fact that makes them bearable:

Not compiling is the normal state of writing Rust. People who write Rust every day see errors dozens of times an hour. The error is not a grade. It is the compiler telling you, precisely, what it needs next.

A Rust error names the file and line, points an arrow at the exact spot, says what it expected and what it found — and often writes the fix out for you under a help: heading. When one appears: read all of it, bottom included, before changing anything.

That's the lesson — free to read. Create a free account to build it: the brief, the grading table and the hint ladder are on the Lab tab.

Create a free account →

Free to start · no credit card · Rust for Beginners is free

The brief, the grading table and the hint ladder live here.

Create a free account to unlock this lab.

Create a free account →

Free to start · no credit card · Rust for Beginners is free

Chat with Guru about the check that is failing you — the written hint ladder on the Lab tab is free and unlimited either way.

Create a free account to unlock this lab.

Create a free account →

Free to start · no credit card · Rust for Beginners is free

Your submitted diff lands here with comments on the exact lines.

Create a free account to unlock this lab.

Create a free account →

Free to start · no credit card · Rust for Beginners is free