10.1Introduction to program-defined types
Everything you've built for nine chapters has used types the language handed you: i32, f64, bool, char, the unit (), tuples, String, &str. They've been enough because the problems were small. Read a number, double it, print it. Roll a die. Find the first word. Each of those programs juggled a handful of loose values, and loose values were fine.
Real programs aren't like that. A program that tracks a customer needs a name, an email, an account balance, and a signup date, and those four facts are not four separate things. They're one thing: a customer. The moment you write a function that takes all four as parameters, you feel the strain.
fn send_receipt(name: &str, email: &str, balance: f64, signup_year: u32) {
// ...
}
Four parameters that must always travel together, in the right order, or the program is wrong in a way the types can't catch. Swap balance and signup_year at one call site and you've mailed a receipt for the year 2026 dollars to an account created in $48.50. Both are numbers; the compiler shrugs. This is the problem custom types solve.
A type you define yourself
Rust lets you bundle related values into a single named type called a struct (short for structure). Here is the customer, as one type instead of four parameters:
struct Customer {
name: String,
email: String,
balance: f64,
signup_year: u32,
}
Customer is now a type, exactly as real as i32. You can store one in a variable, pass one to a function, return one from a function, and put the four fields where they belong: inside the thing they describe. The function signature collapses to a single, honest parameter:
fn send_receipt(customer: &Customer) {
// ...
}
We'll spend this whole chapter learning to define, build, and use structs, and then to attach behavior to them with methods. The next lesson shows the syntax for creating one and reading its fields; this lesson is about why the feature exists and what to call your types.
Program-defined types
A struct is one example of a broader idea. The types built into the language (i32, bool, and friends) are fundamental types, the vocabulary lesson 4.1 introduced. A type you declare in your own code is a program-defined type: it doesn't exist until you write it down, and it exists only for programs that include that definition.
Rust has a few kinds. Structs (this chapter) group several values that are all present at once: a customer has a name and an email and a balance. Enums (chapter 11) describe a value that is one of several alternatives: a traffic light is red or yellow or green, never two at once. Those two kinds, "and" types and "or" types, are the entire foundation, and almost every type you'll ever use, including String itself, is built from them.
Key insight
A struct answers "what pieces make up this thing?" An enum answers "which of these things is it?" Reach for a struct when the parts coexist (a point has both an x and a y); reach for an enum when the value is exactly one of a fixed set of options (chapter 11). Most data modeling in Rust is choosing between these two questions.
Naming your types
The compiler doesn't care what you name a type, but the community has settled the question and the tooling enforces it, the same way lesson 1.8 laid out names for variables and functions. Variables and functions are snake_case; types are UpperCamelCase (also called PascalCase): each word capitalized, no underscores. Customer, BankAccount, HttpResponse, Point.
This isn't only fashion. Because casing is consistent, a Rust reader can tell a type from a value at a glance, without context: Customer is a type, customer is probably a variable holding one. Field names, being values, follow the variable rule and stay snake_case: signup_year, not signupYear or SignupYear.
Name a type after the noun it represents, singular: Customer, not Customers or CustomerData. A value of the type is one customer; a collection of them gets pluralized at the variable, like let customers = .... Pick the most specific accurate noun. Point beats Thing; EmailAddress beats String when the extra precision earns its keep (a technique chapter 10.4 returns to).
struct WeatherReading { // UpperCamelCase type name
city: String, // snake_case field names
temperature_c: f64,
is_raining: bool,
}Best practice
Name types with a singular UpperCamelCase noun and fields with snake_case. If you slip, the non_camel_case_types and non_snake_case lints will warn you, just like the constant-naming warning you met in lesson 5.1. Let the compiler keep you honest.
Why this is the chapter where the course changes gears
The first nine chapters were a strict apprenticeship: the rules of values, ownership, and borrowing, taught on the small set of types the language ships. You earned a lot of compiler errors. The payoff starts here. With structs and (chapter 11) enums, you stop arranging the language's furniture and start building rooms of your own, and every concept you've already paid for, moves, borrows, the four-row parameter table from lesson 9.4, applies unchanged to the types you invent. A &Customer borrows for the same reason a &String does. That's the whole reason the borrowing chapters came first.
Quiz time
Question #1
Why is bundling name, email, and balance into a Customer struct safer than passing them as three separate parameters?
Show solution
Because the three values describe one thing and must always travel together. As separate parameters they can be supplied in the wrong order or partially, and if two share a type (two Strings, say) the compiler can't catch a swap. A single Customer keeps them grouped under one name, so there's one value to pass and no order to get wrong at the call site.
Question #2
Judge each name as a struct type name: conventional, unconventional, or invalid.
a) bank_account b) BankAccount c) Bankaccount d) Bank_Account e) 2Player
Show solution
a) Unconventional: that's the variable/function style; the non_camel_case_types lint warns. b) Conventional UpperCamelCase. c) Unconventional: it compiles, but "account" should be capitalized (BankAccount). d) Unconventional: underscores don't belong in type names. e) Invalid: identifiers can't start with a digit, the rule from lesson 1.8.
Question #3
You're modeling a value that is always exactly one of: Pending, Shipped, or Delivered. Should that be a struct or an enum, and why?
Show solution
An enum. The value is one of a fixed set of alternatives, never two at once, which is the "or" question enums answer. A struct is for things whose parts coexist. Chapter 11 builds exactly this kind of type.
Enough motivation. Next lesson writes a struct, builds one, and reads its fields, with the move and borrow rules from chapters 8 and 9 quietly riding along.