Start learning free

LESSON 207 OF 414 · FUNCTIONS & CLASSES

Defining a Class in Rust: Declaring a struct

Lesson Read the full explanation

TL ; DR:

  • A struct is a blueprint for creating objects. It groups together related data and behavior under a single name.


  • In Rust, you define a struct with the struct keyword, followed by the struct name and a pair of curly braces:

    struct Dog {
    }
    

    The body of the struct can be empty — that's still a valid struct. We'll add data and behavior to it in the next lessons.



Full lesson:

So far, you've worked with built-in types like i32, &str, and arrays. But what if you want to model something the language doesn't have a type for — like a Dog, a BankAccount, or a Game?

That's what structs are for. A struct lets you define your own type by listing what data it holds and what actions it can perform.


Why structs?

Imagine you're writing a program about dogs. Each dog has a name, an age, and can bark. Without structs, you'd have to track each dog's name in one variable, age in another, and write a separate bark() function for each. That gets messy fast.

With a struct, you bundle all of that into one neat package called Dog:

struct Dog {
    // We'll add fields and methods in upcoming lessons.
}

The syntax

Defining an empty struct in Rust looks like this:

struct StructName {
}

Three things to notice:

  • The keyword struct tells Rust you're defining a new type.

  • StructName is the name you choose. By convention, Rust struct names use PascalCase — capitalized words with no underscores: Dog, BankAccount, HighScore.

  • The curly braces { } hold the body of the struct. Unlike C++, Rust does not require a semicolon after the closing brace when the struct has a body.


No classes: struct + impl:

Rust has no classes. You put the data in a struct, and its methods in a separate impl block. Methods take &self; functions without self are associated functions, called as Type::func().

struct Dog {
    name: String,
}

impl Dog {
    fn new(name: String) -> Dog {   // associated function: Dog::new(...)
        Dog { name }
    }
    fn bark(&self) {                // method: takes &self
        println!("{} says woof!", self.name);
    }
}

Your task

Define an empty struct called Dog. Because the pre-filled code creates it with let dog = Dog;, declare it as a unit struct: struct Dog;, with a semicolon and no braces. The pre-filled code in mainFunction will create an instance of your struct and print a confirmation message. If you define the struct correctly, the program will compile and print:

Defined the Dog class!
STEP 1 OF 1
    Ctrl + Enter to check

    RUST · FUNCTIONS & CLASSES

    Defining A Class in Rust, explained

    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 struct says 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, BankAccount or HighScore. A lowercase name like dog still 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 Dog with 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, write Dog {}.
    • 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 new keyword in Rust. A value is created by the struct expression itself, or by an ordinary function such as Dog::new.

    Key takeaways

    • A struct declares a new type; in Rust it takes the place of a class.
    • struct Dog; is a unit struct: no fields, no braces, and Dog on 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 impl blocks, which later lessons add.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free