Chapter 4: Building your own types
Introduces structs and enums as ways to build Rust types and methods as functions attached to those types.
After this chapter you will be able to
- Structs and enums distinction. Distinguish structs, which group values to build a type, from enums, which represent choices rather than groupings.
- Structs versus enums. Distinguish when to use a struct versus an enum: a struct groups properties, while an enum represents a choice among alternatives. Apply that rule of thumb to the book example.
- Three struct forms. Distinguish unit, tuple, and named structs by what each contains and how its fields are written or accessed. Describe the `struct` keyword and the UpperCamelCase naming convention.
- Named struct construction. Explain how to create a value of the `Country` struct, including the need to supply every field and the shorthand available when a variable has the same name as its field.
- Enums as alternative choices. Distinguish an enum from a struct by explaining that a struct groups things together, while an enum represents a choice between possibilities.
- Enum declaration and variant names. Describe how to declare an enum and how to name a chosen variant using the enum name and `::`.
- Data held by variants. Explain that Rust enums can hold data as well as represent choices, including structs or other types.
- Default enum variant numbers. Explain why variants of an enum with no data can be cast to integers and how Rust assigns their numbers by default.
- Custom enum variant numbers. Describe how to choose a variant’s number and how Rust numbers variants that follow without explicit numbers.
- Enum variants in one Vec. Explain how a Vec can contain both u32 and i32 values by storing Number enum values, with one variant carrying a u32 and another carrying an i32.
- Impl blocks and function calls. Explain how an impl block adds functions to a struct or enum, and distinguish functions that take self from associated functions by how they are usually called.
- Struct destructuring pattern. Explain how a let pattern takes field values out of a struct and binds them to variables, reversing the pattern used to create the struct.
- Automatic dereferencing with methods. Explain why a method for String can be called on a &&String using the dot operator, without manually writing * and &.
What it builds on
- Tuples (chapter 3) · Tuple items and their numbered positions.
- Declaring variables and code blocks (chapter 1) · Declaring variables to hold values.
- Vectors (chapter 3) · Vec elements have one type.
- Mutable references (chapter 2) · Mutable references, used here in &mut self.
- Display and Debug (chapter 1) · Printing with Debug formatting.
- More on references (chapter 2) · References and accessing their values
Section by section
Introduction
Introduces structs and enums as ways to build Rust types and methods as functions attached to those types.
- Structs and enums distinction: Distinguish structs, which group values to build a type, from enums, which represent choices rather than groupings.
Why it matters: This distinction gives you a starting point for choosing which kind of type to build. - Methods on custom types: Explain that implementing a struct or enum gives it methods—functions attached to and called on that type.
Why it matters: It connects building a type with giving that type functions you can call.
Key terms: structs, enums, implementing types, methods
4.1 A quick overview of structs and enums
Introduces the distinction between structs and enums using a book’s properties and purchase formats as examples.
- Structs versus enums: Distinguish when to use a struct versus an enum: a struct groups properties, while an enum represents a choice among alternatives. Apply that rule of thumb to the book example.
Why it matters: This rule of thumb helps you decide which kind of type fits the information you want to represent. - Struct and enum combinations: Explain that structs and enums can be used together because either can contain the other.
Why it matters: A type can combine grouped information with a choice without forcing everything into just one kind of type.
Key terms: struct, enum, structs, enums
4.1.1 Structs
This section introduces Rust’s three kinds of structs and shows how to declare and create a named struct.
- Three struct forms: Distinguish unit, tuple, and named structs by what each contains and how its fields are written or accessed. Describe the `struct` keyword and the UpperCamelCase naming convention.
Why it matters: Recognizing the three forms helps you read and choose a struct declaration. - Named struct construction: Explain how to create a value of the `Country` struct, including the need to supply every field and the shorthand available when a variable has the same name as its field.
Why it matters: This lets you construct a complete struct value without unnecessarily repeating field names.
Key terms: struct, unit struct, tuple struct, named struct, UpperCamelCase, Country, fields, instantiate, population, capital
4.1.2 Enums
This section introduces enums as choices between variants, shows how to name and use variants, and explains that variants can hold data.
- Enums as alternative choices: Distinguish an enum from a struct by explaining that a struct groups things together, while an enum represents a choice between possibilities.
Why it matters: This distinction helps you choose the right kind of type for your data. - Enum declaration and variant names: Describe how to declare an enum and how to name a chosen variant using the enum name and `::`.
Why it matters: This is the basic syntax for defining and selecting enum choices. - Data held by variants: Explain that Rust enums can hold data as well as represent choices, including structs or other types.
Why it matters: An enum choice can carry information related to that choice. - Shorter names with use: Describe how `use` can avoid repeatedly writing an enum’s name before its variants, and note that it can shorten other `::` paths too.
Why it matters: Imports make repeated names and paths less cumbersome to write.
Key terms: enum, struct, variant, ::, Climate::Tropical, data, use, import, *
4.1.3 Casting enums into integers
This section explains how data-free enum variants get integer values and how to assign different values to them.
- Default enum variant numbers: Explain why variants of an enum with no data can be cast to integers and how Rust assigns their numbers by default.
Why it matters: It explains where the integer comes from when you cast a data-free enum variant. - Custom enum variant numbers: Describe how to choose a variant’s number and how Rust numbers variants that follow without explicit numbers.
Why it matters: It lets you predict variant numbers when an enum mixes chosen and automatically assigned values.
Key terms: enum, variant, cast, compiler
4.1.4 Enums to use multiple types
This section shows how an enum can let one Vec hold values of different underlying types while keeping a single element type.
- Enum variants in one Vec: Explain how a Vec can contain both u32 and i32 values by storing Number enum values, with one variant carrying a u32 and another carrying an i32.
Why it matters: This lets you put either kind of number in one Vec without changing the rule that a Vec has a single element type.
Key terms: Vec<Number>, enum, variant, u32, i32
4.1.5 Implementing structs and enums
This section shows how impl blocks add behavior to structs and enums, distinguishes methods from associated functions, and introduces Debug derivation and Self versus self.
- Impl blocks and function calls: Explain how an impl block adds functions to a struct or enum, and distinguish functions that take self from associated functions by how they are usually called.
Why it matters: This distinction helps you read and write the functions attached to your own Rust types. - Self and self distinction: Distinguish Self, which names the current type, from self, which refers to the particular object in a method.
Why it matters: The capitalization changes whether the code names the type or the object a method acts on. - Debug derivation for printing: Explain how to make a struct or enum printable with {:?} by placing #[derive(Debug)] above its definition.
Why it matters: It lets you print values of the types you create while working with them.
Key terms: impl, impl block, methods, associated functions, self, ::, &mut self, Debug, {:?}, #[derive(Debug)]
4.2 Destructuring
This section shows how to take values out of a struct with destructuring, including renaming fields, ignoring unwanted fields, and destructuring in a function signature.
- Struct destructuring pattern: Explain how a let pattern takes field values out of a struct and binds them to variables, reversing the pattern used to create the struct.
Why it matters: It lets you use a struct’s field values as separate variables. - Renamed and skipped fields: Describe how to choose different variable names while destructuring and how to use `..` when only some struct fields are wanted.
Why it matters: You can give extracted values useful names without binding fields you will not use. - Function signature destructuring: State that a struct value can be destructured directly in a function signature.
Why it matters: It gives you another place to take apart a struct when writing a function.
Key terms: destructuring, let, struct, destructure, .., signature of a function
4.3 References and the dot operator
This section contrasts explicit dereferencing in comparisons with the automatic dereferencing provided by the dot operator when calling methods, then connects familiar library types to structs and impl blocks.
- Dereferencing for value comparisons: Explain why comparing an i32 with an &i32 fails and how dereferencing the reference makes the comparison work.
Why it matters: It explains why a comparison can require * even when the reference points to the value you want. - Automatic dereferencing with methods: Explain why a method for String can be called on a &&String using the dot operator, without manually writing * and &.
Why it matters: It lets you call methods on references without manually working through each reference layer. - Familiar types as structs: Describe how String and Vec relate to the structs and impl blocks introduced in this chapter.
Why it matters: It connects types you have already used to the structs and methods you can define yourself.
Key terms: reference, dereferencing, *, dot operator, .is_empty(), dereference, &self, String, Vec, struct
4.4 Summary
Where this is used later
- (chapter intro) (chapter 5) · Introduces enums.
- Importing and renaming inside a function (chapter 11) · Returns to importing and renaming items inside a function.
- Option (chapter 5) · Recognizing that enum variants can carry data helps with understanding Option.
- Traits: The basics (chapter 7) · Methods implemented on structs and enums.
- All you need are the method signatures (chapter 7) · Assumes familiarity with implementing behavior for a struct.
- Writing builder methods (chapter 15) · Implementing methods on structs.
- Destructuring and mapping arrays (chapter 20) · Applies the idea of destructuring to arrays.
- Associated functions (chapter 20) · Earlier use of references and the dot operator.
Concepts explained in this chapter
- enumAn enum is a Rust user-defined type whose values are one of a fixed set of named variants, optionally carrying associated data.
- structA struct is a composite data type or structure that groups related fields or values into a single unit.
- methodA method is a function associated with a type that is invoked through an instance or type and may receive the instance through self, &self, or &mut self.
- impl blockAn impl block is Rust syntax that defines methods or trait implementations associated with a type.
- attributeA struct field is a named or positional component of a Rust struct whose visibility can be declared independently of the struct type.
- dotted notationDot notation is Rust syntax that accesses a tuple element or object member by placing a period before its selector.
- derive macroA derive attribute is a Rust attribute that requests automatic implementation of specified traits for a type.
- destructuring assignmentDestructuring is Rust syntax that binds the components of a composite value to separate variables through a matching pattern.
- int32An int32 is a signed integer type represented using exactly 32 bits.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
