Why you define your own types
Rust ships with types such as i32, &str and arrays, but a program often needs to talk about things the language has no type for: a dog, a bank account, a game. A struct lets you invent that type. It is the blueprint from which values of that type are made, and it gives the concept a single name instead of a pile of loose variables.
Rust has no class keyword. A struct plays the role of the class, and the functions that belong to it go in a separate impl block. This lesson only needs the first half: declaring the type.
The syntax
The simplest struct has no fields. It can be written in two ways:
struct Dog;
struct Dog {
}
The first form is called a unit struct. It is one line, with a semicolon and no braces. The second has braces with nothing inside. Both declare a valid type, and both are placeholders for fields you add later.
Points to notice:
- The keyword
structsays you are defining a new type. - The name is yours to choose. By convention, struct names use PascalCase: capitalised words with no underscores, such as
Dog,BankAccountorHighScore. A lowercase name likedogstill compiles, but the compiler warns that the type should have an upper camel case name. - A struct with a braced body takes no semicolon after the closing brace. Writing
struct Dog {};is an error: the compiler reports "expected item, found;". The unit form is the opposite: it ends with a semicolon because it has no body.
Creating a value of the type
The two forms differ in how you write an instance:
- For a unit struct, the name by itself is a complete value:
let dog = Dog;. - For a braced struct, you write the braces:
let dog = Dog {};.
The braced expression also works for a unit struct, so Dog {} is accepted for both declarations. The reverse does not hold: with struct Dog {}, writing let dog = Dog; fails with "expected value, found struct Dog". The two lines have to match the declaration.
The lesson's exercise
The starter code has a function mainFunction that already contains let dog = Dog; and a println!, but no Dog exists yet. Without a declaration, the compiler says it cannot find the value Dog in this scope.
The task is one step: at the top of the file, declare a unit struct named Dog. The pre-filled line let dog = Dog; is written for the unit form, which is why the declaration has to be the one-line version with a semicolon.
The solution in Rust
struct Dog;
fn mainFunction() {
let dog = Dog;
println!("Defined the Dog class!");
}
When mainFunction runs, it prints:
Defined the Dog class!
Two compiler warnings appear for this code, and neither is an error. The variable dog is never used afterwards, so the compiler suggests naming it _dog. And mainFunction is written in camelCase while Rust style is snake_case. The program compiles and runs regardless.
Where the methods go
An empty struct is a start. Real structs hold data in fields, and their behaviour is written in an impl block:
struct Dog {
name: String,
}
impl Dog {
fn new(name: String) -> Dog {
Dog { name }
}
fn bark(&self) {
println!("{} says woof!", self.name);
}
}
Here new has no self parameter, so it is called on the type: Dog::new(String::from("Rex")). bark takes &self, so it is called on a value: d.bark() prints Rex says woof!. Later lessons build these pieces one at a time.
Common mistakes
- Forgetting the semicolon on a unit struct.
struct Dogwith nothing after it does not compile. - Adding a semicolon to a braced struct.
struct Dog {};is rejected. - Mismatching declaration and use.
let dog = Dog;needs the unit form. With braces in the declaration, writeDog {}. - Declaring the struct after the function. That actually works in Rust, because items can appear in any order in a file. Placing it at the top is a matter of readability.
- Looking for a constructor keyword. There is no
newkeyword in Rust. A value is created by the struct expression itself, or by an ordinary function such asDog::new.
Key takeaways
- A
structdeclares a new type; in Rust it takes the place of a class. struct Dog;is a unit struct: no fields, no braces, andDogon its own is its value.- A braced struct has no trailing semicolon, and its values are written
Dog {}. - Struct names use PascalCase.
- Data goes in the struct and methods go in
implblocks, which later lessons add.
