diff options
Diffstat (limited to '')
-rw-r--r-- | src/doc/book/nostarch/chapter05.md | 1073 |
1 files changed, 1073 insertions, 0 deletions
diff --git a/src/doc/book/nostarch/chapter05.md b/src/doc/book/nostarch/chapter05.md new file mode 100644 index 000000000..356ba82af --- /dev/null +++ b/src/doc/book/nostarch/chapter05.md @@ -0,0 +1,1073 @@ +<!-- DO NOT EDIT THIS FILE. + +This file is periodically generated from the content in the `/src/` +directory, so all fixes need to be made in `/src/`. +--> + +[TOC] + +# Using Structs to Structure Related Data + +A *struct*, or *structure*, is a custom data type that lets you package +together and name multiple related values that make up a meaningful group. If +you’re familiar with an object-oriented language, a *struct* is like an +object’s data attributes. In this chapter, we’ll compare and contrast tuples +with structs to build on what you already know and demonstrate when structs are +a better way to group data. + +We’ll demonstrate how to define and instantiate structs. We’ll discuss how to +define associated functions, especially the kind of associated functions called +*methods*, to specify behavior associated with a struct type. Structs and enums +(discussed in Chapter 6) are the building blocks for creating new types in your +program’s domain to take full advantage of Rust’s compile time type checking. + +## Defining and Instantiating Structs + +Structs are similar to tuples, discussed in “The Tuple Type” section, in that +both hold multiple related values. Like tuples, the pieces of a struct can be +different types. Unlike with tuples, in a struct you’ll name each piece of data +so it’s clear what the values mean. Adding these names means that structs are +more flexible than tuples: you don’t have to rely on the order of the data to +specify or access the values of an instance. + +To define a struct, we enter the keyword `struct` and name the entire struct. A +struct’s name should describe the significance of the pieces of data being +grouped together. Then, inside curly brackets, we define the names and types of +the pieces of data, which we call *fields*. For example, Listing 5-1 shows a +struct that stores information about a user account. + +``` +struct User { + active: bool, + username: String, + email: String, + sign_in_count: u64, +} +``` + +Listing 5-1: A `User` struct definition + +To use a struct after we’ve defined it, we create an *instance* of that struct +by specifying concrete values for each of the fields. We create an instance by +stating the name of the struct and then add curly brackets containing `key: +value` pairs, where the keys are the names of the fields and the values are the +data we want to store in those fields. We don’t have to specify the fields in +the same order in which we declared them in the struct. In other words, the +struct definition is like a general template for the type, and instances fill +in that template with particular data to create values of the type. For +example, we can declare a particular user as shown in Listing 5-2. + +``` +fn main() { + let user1 = User { + email: String::from("someone@example.com"), + username: String::from("someusername123"), + active: true, + sign_in_count: 1, + }; +} +``` + +Listing 5-2: Creating an instance of the `User` struct + +To get a specific value from a struct, we use dot notation. For example, to +access this user’s email address, we use `user1.email`. If the instance is +mutable, we can change a value by using the dot notation and assigning into a +particular field. Listing 5-3 shows how to change the value in the `email` +field of a mutable `User` instance. + +<!--- Do we want to mention that `user1.email` will move the field? We can't +just use `user1.email` multiple times re: "wherever we wanted +to use this value" +/JT ---> +<!-- I don't really want to mention that, but I did reword to avoid the +implication that we can use the value wherever we wanted to. /Carol --> + +``` +fn main() { + let mut user1 = User { + email: String::from("someone@example.com"), + username: String::from("someusername123"), + active: true, + sign_in_count: 1, + }; + + user1.email = String::from("anotheremail@example.com"); +} +``` + +Listing 5-3: Changing the value in the `email` field of a `User` instance + +Note that the entire instance must be mutable; Rust doesn’t allow us to mark +only certain fields as mutable. As with any expression, we can construct a new +instance of the struct as the last expression in the function body to +implicitly return that new instance. + +Listing 5-4 shows a `build_user` function that returns a `User` instance with +the given email and username. The `active` field gets the value of `true`, and +the `sign_in_count` gets a value of `1`. + +``` +fn build_user(email: String, username: String) -> User { + User { + email: email, + username: username, + active: true, + sign_in_count: 1, + } +} +``` + +Listing 5-4: A `build_user` function that takes an email and username and +returns a `User` instance + +It makes sense to name the function parameters with the same name as the struct +fields, but having to repeat the `email` and `username` field names and +variables is a bit tedious. If the struct had more fields, repeating each name +would get even more annoying. Luckily, there’s a convenient shorthand! + +### Using the Field Init Shorthand + +Because the parameter names and the struct field names are exactly the same in +Listing 5-4, we can use the *field init shorthand* syntax to rewrite +`build_user` so that it behaves exactly the same but doesn’t have the +repetition of `email` and `username`, as shown in Listing 5-5. + +``` +fn build_user(email: String, username: String) -> User { + User { + email, + username, + active: true, + sign_in_count: 1, + } +} +``` + +Listing 5-5: A `build_user` function that uses field init shorthand because the +`email` and `username` parameters have the same name as struct fields + +Here, we’re creating a new instance of the `User` struct, which has a field +named `email`. We want to set the `email` field’s value to the value in the +`email` parameter of the `build_user` function. Because the `email` field and +the `email` parameter have the same name, we only need to write `email` rather +than `email: email`. + +### Creating Instances From Other Instances With Struct Update Syntax + +It’s often useful to create a new instance of a struct that includes most of +the values from another instance, but changes some. You can do this using +*struct update syntax*. + +First, in Listing 5-6 we show how to create a new `User` instance in `user2` +regularly, without the update syntax. We set a new value for `email` but +otherwise use the same values from `user1` that we created in Listing 5-2. + +``` +fn main() { + // --snip-- + + let user2 = User { + active: user1.active, + username: user1.username, + email: String::from("another@example.com"), + sign_in_count: user1.sign_in_count, + }; +} +``` + +Listing 5-6: Creating a new `User` instance using one of the values from `user1` + +Using struct update syntax, we can achieve the same effect with less code, as +shown in Listing 5-7. The syntax `..` specifies that the remaining fields not +explicitly set should have the same value as the fields in the given instance. + +``` +fn main() { + // --snip-- + + let user2 = User { + email: String::from("another@example.com"), + ..user1 + }; +} +``` + +Listing 5-7: Using struct update syntax to set a new `email` value for a `User` +instance but use the rest of the values from `user1` + +The code in Listing 5-7 also creates an instance in `user2` that has a +different value for `email` but has the same values for the `username`, +`active`, and `sign_in_count` fields from `user1`. The `..user1` must come last +to specify that any remaining fields should get their values from the +corresponding fields in `user1`, but we can choose to specify values for as +many fields as we want in any order, regardless of the order of the fields in +the struct’s definition. + +Note that the struct update syntax uses `=` like an assignment; this is +because it moves the data, just as we saw in the “Ways Variables and Data +Interact: Move” section. In this example, we can no longer use `user1` after +creating `user2` because the `String` in the `username` field of `user1` was +moved into `user2`. If we had given `user2` new `String` values for both +`email` and `username`, and thus only used the `active` and `sign_in_count` +values from `user1`, then `user1` would still be valid after creating `user2`. +The types of `active` and `sign_in_count` are types that implement the `Copy` +trait, so the behavior we discussed in the “Stack-Only Data: Copy” section +would apply. + +<!--- Misspelled "assignment" above. +/JT ---> +<!-- Fixed! /Carol --> + +### Using Tuple Structs without Named Fields to Create Different Types + +Rust also supports structs that look similar to tuples, called *tuple +structs*. Tuple structs have the added meaning the struct name provides but +don’t have names associated with their fields; rather, they just have the types +of the fields. Tuple structs are useful when you want to give the whole tuple a +name and make the tuple a different type from other tuples, and when naming each +field as in a regular struct would be verbose or redundant. + +To define a tuple struct, start with the `struct` keyword and the struct name +followed by the types in the tuple. For example, here we define and use +two tuple structs named `Color` and `Point`: + +``` +struct Color(i32, i32, i32); +struct Point(i32, i32, i32); + +fn main() { + let black = Color(0, 0, 0); + let origin = Point(0, 0, 0); +} +``` + +Note that the `black` and `origin` values are different types, because they’re +instances of different tuple structs. Each struct you define is its own type, +even though the fields within the struct might have the same types. For +example, a function that takes a parameter of type `Color` cannot take a +`Point` as an argument, even though both types are made up of three `i32` +values. Otherwise, tuple struct instances are similar to tuples in that you can +destructure them into their individual pieces, and you can use a `.` followed +by the index to access an individual value. + +<!--- The last line above feels a bit misleading. There are related +restrictions on tuple structs that don't apply to tuples. + +One example is you can't create a tuple struct with a tuple. +``` +struct Color(i32, i32, i32); + +fn main() { + let x: Color = (1, 2, 3); +} +``` + +You can't pass a tuple struct to something that expects a tuple, either. +/JT ---> +<!-- I've reworded to avoid that implication /Carol --> + +### Unit-Like Structs Without Any Fields + +You can also define structs that don’t have any fields! These are called +*unit-like structs* because they behave similarly to `()`, the unit type that +we mentioned in “The Tuple Type” section. Unit-like structs can be useful when +you need to implement a trait on some type but don’t have any data that you +want to store in the type itself. We’ll discuss traits in Chapter 10. Here’s an +example of declaring and instantiating a unit struct named `AlwaysEqual`: + +``` +struct AlwaysEqual; + +fn main() { + let subject = AlwaysEqual; +} +``` + +To define `AlwaysEqual`, we use the `struct` keyword, the name we want, then a +semicolon. No need for curly brackets or parentheses! Then we can get an +instance of `AlwaysEqual` in the `subject` variable in a similar way: using the +name we defined, without any curly brackets or parentheses. Imagine that later +we’ll implement behavior for this type such that every instance of +`AlwaysEqual` is always equal to every instance of any other type, perhaps to +have a known result for testing purposes. We wouldn’t need any data to +implement that behavior! You’ll see in Chapter 10 how to define traits and +implement them on any type, including unit-like structs. + +> ### Ownership of Struct Data +> +> In the `User` struct definition in Listing 5-1, we used the owned `String` +> type rather than the `&str` string slice type. This is a deliberate choice +> because we want each instance of this struct to own all of its data and for +> that data to be valid for as long as the entire struct is valid. +> +> It’s also possible for structs to store references to data owned by something +> else, but to do so requires the use of *lifetimes*, a Rust feature that we’ll +> discuss in Chapter 10. Lifetimes ensure that the data referenced by a struct +> is valid for as long as the struct is. Let’s say you try to store a reference +> in a struct without specifying lifetimes, like the following; this won’t work: +> +> Filename: src/main.rs +> +> ``` +> struct User { +> username: &str, +> email: &str, +> sign_in_count: u64, +> active: bool, +> } +> +> fn main() { +> let user1 = User { +> email: "someone@example.com", +> username: "someusername123", +> active: true, +> sign_in_count: 1, +> }; +> } +> ``` +> +> The compiler will complain that it needs lifetime specifiers: +> +> ``` +> $ cargo run +> Compiling structs v0.1.0 (file:///projects/structs) +> error[E0106]: missing lifetime specifier +> --> src/main.rs:3:15 +> | +> 3 | username: &str, +> | ^ expected named lifetime parameter +> | +> help: consider introducing a named lifetime parameter +> | +> 1 ~ struct User<'a> { +> 2 | active: bool, +> 3 ~ username: &'a str, +> | +> +> error[E0106]: missing lifetime specifier +> --> src/main.rs:4:12 +> | +> 4 | email: &str, +> | ^ expected named lifetime parameter +> | +> help: consider introducing a named lifetime parameter +> | +> 1 ~ struct User<'a> { +> 2 | active: bool, +> 3 | username: &str, +> 4 ~ email: &'a str, +> | +> ``` +> +> In Chapter 10, we’ll discuss how to fix these errors so you can store +> references in structs, but for now, we’ll fix errors like these using owned +> types like `String` instead of references like `&str`. + +## An Example Program Using Structs + +To understand when we might want to use structs, let’s write a program that +calculates the area of a rectangle. We’ll start by using single variables, and +then refactor the program until we’re using structs instead. + +Let’s make a new binary project with Cargo called *rectangles* that will take +the width and height of a rectangle specified in pixels and calculate the area +of the rectangle. Listing 5-8 shows a short program with one way of doing +exactly that in our project’s *src/main.rs*. + +Filename: src/main.rs + +``` +fn main() { + let width1 = 30; + let height1 = 50; + + println!( + "The area of the rectangle is {} square pixels.", + area(width1, height1) + ); +} + +fn area(width: u32, height: u32) -> u32 { + width * height +} +``` + +Listing 5-8: Calculating the area of a rectangle specified by separate width +and height variables + +Now, run this program using `cargo run`: + +``` +$ cargo run + Compiling rectangles v0.1.0 (file:///projects/rectangles) + Finished dev [unoptimized + debuginfo] target(s) in 0.42s + Running `target/debug/rectangles` +The area of the rectangle is 1500 square pixels. +``` + +This code succeeds in figuring out the area of the rectangle by calling the +`area` function with each dimension, but we can do more to make this code clear +and readable. + +The issue with this code is evident in the signature of `area`: + +``` +fn area(width: u32, height: u32) -> u32 { +``` + +The `area` function is supposed to calculate the area of one rectangle, but the +function we wrote has two parameters, and it's not clear anywhere in our +program that the parameters are related. It would be more readable and more +manageable to group width and height together. We’ve already discussed one way +we might do that in “The Tuple Type” section of Chapter 3: by using tuples. + +### Refactoring with Tuples + +Listing 5-9 shows another version of our program that uses tuples. + +Filename: src/main.rs + +``` +fn main() { + let rect1 = (30, 50); + + println!( + "The area of the rectangle is {} square pixels.", + area(rect1) + ); +} + +fn area(dimensions: (u32, u32)) -> u32 { + dimensions.0 * dimensions.1 +} +``` + +Listing 5-9: Specifying the width and height of the rectangle with a tuple + +In one way, this program is better. Tuples let us add a bit of structure, and +we’re now passing just one argument. But in another way, this version is less +clear: tuples don’t name their elements, so we have to index into the parts of +the tuple, making our calculation less obvious. + +Mixing up the width and height wouldn’t matter for the area calculation, but if +we want to draw the rectangle on the screen, it would matter! We would have to +keep in mind that `width` is the tuple index `0` and `height` is the tuple +index `1`. This would be even harder for someone else to figure out and keep in +mind if they were to use our code. Because we haven’t conveyed the meaning of +our data in our code, it’s now easier to introduce errors. + +### Refactoring with Structs: Adding More Meaning + +We use structs to add meaning by labeling the data. We can transform the tuple +we’re using into a struct with a name for the whole as well as names for the +parts, as shown in Listing 5-10. + +Filename: src/main.rs + +``` +struct Rectangle { + width: u32, + height: u32, +} + +fn main() { + let rect1 = Rectangle { + width: 30, + height: 50, + }; + + println!( + "The area of the rectangle is {} square pixels.", + area(&rect1) + ); +} + +fn area(rectangle: &Rectangle) -> u32 { + rectangle.width * rectangle.height +} +``` + +Listing 5-10: Defining a `Rectangle` struct + +Here we’ve defined a struct and named it `Rectangle`. Inside the curly +brackets, we defined the fields as `width` and `height`, both of which have +type `u32`. Then in `main`, we created a particular instance of `Rectangle` +that has a width of 30 and a height of 50. + +Our `area` function is now defined with one parameter, which we’ve named +`rectangle`, whose type is an immutable borrow of a struct `Rectangle` +instance. As mentioned in Chapter 4, we want to borrow the struct rather than +take ownership of it. This way, `main` retains its ownership and can continue +using `rect1`, which is the reason we use the `&` in the function signature and +where we call the function. + +The `area` function accesses the `width` and `height` fields of the `Rectangle` +instance (note that accessing fields of a borrowed struct instance does not +move the field values, which is why you often see borrows of structs). Our +function signature for `area` now says exactly what we mean: calculate the area +of `Rectangle`, using its `width` and `height` fields. This conveys that the +width and height are related to each other, and it gives descriptive names to +the values rather than using the tuple index values of `0` and `1`. This is a +win for clarity. + +<!--- Tying to my comment above about `user1.email` moving that field: we should +take a minute here and explain that accessing fields on a borrowed struct does +not move them, and why you often see borrows of structs. +/JT ---> +<!-- I've added a note in the paragraph above; I haven't really seen people +struggle with that concept though so I don't want to spend too much time on it +/Carol --> + +### Adding Useful Functionality with Derived Traits + +It’d be useful to be able to print an instance of `Rectangle` while we’re +debugging our program and see the values for all its fields. Listing 5-11 tries +using the `println!` macro as we have used in previous chapters. This won’t +work, however. + +Filename: src/main.rs + +``` +struct Rectangle { + width: u32, + height: u32, +} + +fn main() { + let rect1 = Rectangle { + width: 30, + height: 50, + }; + + println!("rect1 is {}", rect1); +} +``` + +Listing 5-11: Attempting to print a `Rectangle` instance + +When we compile this code, we get an error with this core message: + +``` +error[E0277]: `Rectangle` doesn't implement `std::fmt::Display` +``` + +The `println!` macro can do many kinds of formatting, and by default, the curly +brackets tell `println!` to use formatting known as `Display`: output intended +for direct end user consumption. The primitive types we’ve seen so far +implement `Display` by default, because there’s only one way you’d want to show +a `1` or any other primitive type to a user. But with structs, the way +`println!` should format the output is less clear because there are more +display possibilities: Do you want commas or not? Do you want to print the +curly brackets? Should all the fields be shown? Due to this ambiguity, Rust +doesn’t try to guess what we want, and structs don’t have a provided +implementation of `Display` to use with `println!` and the `{}` placeholder. + +If we continue reading the errors, we’ll find this helpful note: + +``` += help: the trait `std::fmt::Display` is not implemented for `Rectangle` += note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead +``` + +Let’s try it! The `println!` macro call will now look like `println!("rect1 is +{:?}", rect1);`. Putting the specifier `:?` inside the curly brackets tells +`println!` we want to use an output format called `Debug`. The `Debug` trait +enables us to print our struct in a way that is useful for developers so we can +see its value while we’re debugging our code. + +Compile the code with this change. Drat! We still get an error: + +``` +error[E0277]: `Rectangle` doesn't implement `Debug` +``` + +But again, the compiler gives us a helpful note: + +``` += help: the trait `Debug` is not implemented for `Rectangle` += note: add `#[derive(Debug)]` or manually implement `Debug` +``` + +Rust *does* include functionality to print out debugging information, but we +have to explicitly opt in to make that functionality available for our struct. +To do that, we add the outer attribute `#[derive(Debug)]` just before the +struct definition, as shown in Listing 5-12. + +Filename: src/main.rs + +``` +#[derive(Debug)] +struct Rectangle { + width: u32, + height: u32, +} + +fn main() { + let rect1 = Rectangle { + width: 30, + height: 50, + }; + + println!("rect1 is {:?}", rect1); +} +``` + +Listing 5-12: Adding the attribute to derive the `Debug` trait and printing the +`Rectangle` instance using debug formatting + +Now when we run the program, we won’t get any errors, and we’ll see the +following output: + +``` +$ cargo run + Compiling rectangles v0.1.0 (file:///projects/rectangles) + Finished dev [unoptimized + debuginfo] target(s) in 0.48s + Running `target/debug/rectangles` +rect1 is Rectangle { width: 30, height: 50 } +``` + +Nice! It’s not the prettiest output, but it shows the values of all the fields +for this instance, which would definitely help during debugging. When we have +larger structs, it’s useful to have output that’s a bit easier to read; in +those cases, we can use `{:#?}` instead of `{:?}` in the `println!` string. +In this example, using the `{:#?}` style will output: + +``` +$ cargo run + Compiling rectangles v0.1.0 (file:///projects/rectangles) + Finished dev [unoptimized + debuginfo] target(s) in 0.48s + Running `target/debug/rectangles` +rect1 is Rectangle { + width: 30, + height: 50, +} +``` + +Another way to print out a value using the `Debug` format is to use the `dbg!` +macro, which takes ownership of an expression (as opposed to `println!` that +takes a reference), prints the file and line number of where that `dbg!` macro +call occurs in your code along with the resulting value of that expression, and +returns ownership of the value. + +> Note: Calling the `dbg!` macro prints to the standard error console stream +> (`stderr`), as opposed to `println!` which prints to the standard output +> console stream (`stdout`). We’ll talk more about `stderr` and `stdout` in the +> “Writing Error Messages to Standard Error Instead of Standard Output” section +> in Chapter 12. + +Here’s an example where we’re interested in the value that gets assigned to the +`width` field, as well as the value of the whole struct in `rect1`: + +``` +#[derive(Debug)] +struct Rectangle { + width: u32, + height: u32, +} + +fn main() { + let scale = 2; + let rect1 = Rectangle { + width: dbg!(30 * scale), + height: 50, + }; + + dbg!(&rect1); +} +``` + +We can put `dbg!` around the expression `30 * scale` and, because `dbg!` +returns ownership of the expression’s value, the `width` field will get the +same value as if we didn’t have the `dbg!` call there. We don’t want `dbg!` to +take ownership of `rect1`, so we use a reference to `rect1` in the next call. +Here’s what the output of this example looks like: + +<!--- is it worth calling out that println! doesn't have the dbg! shortcoming +of taking ownership? +/JT ---> +<!-- I added a note in the paragraph above that starts with "Another way to +print out a value" /Carol --> + +``` +$ cargo run + Compiling rectangles v0.1.0 (file:///projects/rectangles) + Finished dev [unoptimized + debuginfo] target(s) in 0.61s + Running `target/debug/rectangles` +[src/main.rs:10] 30 * scale = 60 +[src/main.rs:14] &rect1 = Rectangle { + width: 60, + height: 50, +} +``` + +We can see the first bit of output came from *src/main.rs* line 10, where we’re +debugging the expression `30 * scale`, and its resulting value is 60 (the +`Debug` formatting implemented for integers is to print only their value). The +`dbg!` call on line 14 of *src/main.rs* outputs the value of `&rect1`, which is +the `Rectangle` struct. This output uses the pretty `Debug` formatting of the +`Rectangle` type. The `dbg!` macro can be really helpful when you’re trying to +figure out what your code is doing! + +In addition to the `Debug` trait, Rust has provided a number of traits for us +to use with the `derive` attribute that can add useful behavior to our custom +types. Those traits and their behaviors are listed in Appendix C. We’ll cover +how to implement these traits with custom behavior as well as how to create +your own traits in Chapter 10. There are also many attributes other than +`derive`; for more information, see the “Attributes” section of the Rust +Reference at *https://doc.rust-lang.org/reference/attributes.html*. + +Our `area` function is very specific: it only computes the area of rectangles. +It would be helpful to tie this behavior more closely to our `Rectangle` +struct, because it won’t work with any other type. Let’s look at how we can +continue to refactor this code by turning the `area` function into an `area` +*method* defined on our `Rectangle` type. + +## Method Syntax + +*Methods* are similar to functions: we declare them with the `fn` keyword and a +name, they can have parameters and a return value, and they contain some code +that’s run when the method is called from somewhere else. Unlike functions, +methods are defined within the context of a struct (or an enum or a trait +object, which we cover in Chapters 6 and 17, respectively), and their first +parameter is always `self`, which represents the instance of the struct the +method is being called on. + +<!--- minor nit: some folks call the non-self functions in an `impl` +"static methods" as a nod to OO languages that do the same. For folks +from that background, we may want to call out that instance methods always +have `self` and methods on the type do not. +/JT ---> +<!-- This paragraph already says "their first parameter is always `self`", and +we get into associated functions in just a bit. I don't want to distract with +that info here; not changing anything at this spot. /Carol --> + +### Defining Methods + +Let’s change the `area` function that has a `Rectangle` instance as a parameter +and instead make an `area` method defined on the `Rectangle` struct, as shown +in Listing 5-13. + +Filename: src/main.rs + +``` +#[derive(Debug)] +struct Rectangle { + width: u32, + height: u32, +} + +impl Rectangle { + fn area(&self) -> u32 { + self.width * self.height + } +} + +fn main() { + let rect1 = Rectangle { + width: 30, + height: 50, + }; + + println!( + "The area of the rectangle is {} square pixels.", + rect1.area() + ); +} +``` + +Listing 5-13: Defining an `area` method on the `Rectangle` struct + +To define the function within the context of `Rectangle`, we start an `impl` +(implementation) block for `Rectangle`. Everything within this `impl` block +will be associated with the `Rectangle` type. Then we move the `area` function +within the `impl` curly brackets and change the first (and in this case, only) +parameter to be `self` in the signature and everywhere within the body. In +`main`, where we called the `area` function and passed `rect1` as an argument, +we can instead use *method syntax* to call the `area` method on our `Rectangle` +instance. The method syntax goes after an instance: we add a dot followed by +the method name, parentheses, and any arguments. + +In the signature for `area`, we use `&self` instead of `rectangle: &Rectangle`. +The `&self` is actually short for `self: &Self`. Within an `impl` block, the +type `Self` is an alias for the type that the `impl` block is for. Methods must +have a parameter named `self` of type `Self` for their first parameter, so Rust +lets you abbreviate this with only the name `self` in the first parameter spot. +Note that we still need to use the `&` in front of the `self` shorthand to +indicate this method borrows the `Self` instance, just as we did in `rectangle: +&Rectangle`. Methods can take ownership of `self`, borrow `self` immutably as +we’ve done here, or borrow `self` mutably, just as they can any other parameter. + +We’ve chosen `&self` here for the same reason we used `&Rectangle` in the +function version: we don’t want to take ownership, and we just want to read the +data in the struct, not write to it. If we wanted to change the instance that +we’ve called the method on as part of what the method does, we’d use `&mut +self` as the first parameter. Having a method that takes ownership of the +instance by using just `self` as the first parameter is rare; this technique is +usually used when the method transforms `self` into something else and you want +to prevent the caller from using the original instance after the transformation. + +The main reason for using methods instead of functions, in addition to providing +method syntax and not having to repeat the type of `self` in every method’s +signature, is for organization. We’ve put all the things we can do with an +instance of a type in one `impl` block rather than making future users of our +code search for capabilities of `Rectangle` in various places in the library we +provide. + +Note that we can choose to give a method the same name as one of the struct’s +fields. For example, we can define a method on `Rectangle` also named `width`: + +Filename: src/main.rs + +``` +impl Rectangle { + fn width(&self) -> bool { + self.width > 0 + } +} + +fn main() { + let rect1 = Rectangle { + width: 30, + height: 50, + }; + + if rect1.width() { + println!("The rectangle has a nonzero width; it is {}", rect1.width); + } +} +``` + +Here, we’re choosing to make the `width` method return `true` if the value in +the instance’s `width` field is greater than 0, and `false` if the value is 0: +we can use a field within a method of the same name for any purpose. In `main`, +when we follow `rect1.width` with parentheses, Rust knows we mean the method +`width`. When we don’t use parentheses, Rust knows we mean the field `width`. + +Often, but not always, when we give methods with the same name as a field we +want it to only return the value in the field and do nothing else. Methods like +this are called *getters*, and Rust does not implement them automatically for +struct fields as some other languages do. Getters are useful because you can +make the field private but the method public and thus enable read-only access +to that field as part of the type’s public API. We will be discussing what +public and private are and how to designate a field or method as public or +private in Chapter 7. + +> ### Where’s the `->` Operator? +> +> In C and C++, two different operators are used for calling methods: you use +> `.` if you’re calling a method on the object directly and `->` if you’re +> calling the method on a pointer to the object and need to dereference the +> pointer first. In other words, if `object` is a pointer, +> `object->something()` is similar to `(*object).something()`. +> +> Rust doesn’t have an equivalent to the `->` operator; instead, Rust has a +> feature called *automatic referencing and dereferencing*. Calling methods is +> one of the few places in Rust that has this behavior. +> +> Here’s how it works: when you call a method with `object.something()`, Rust +> automatically adds in `&`, `&mut`, or `*` so `object` matches the signature of +> the method. In other words, the following are the same: +> +> +> ``` +> p1.distance(&p2); +> (&p1).distance(&p2); +> ``` +> +> The first one looks much cleaner. This automatic referencing behavior works +> because methods have a clear receiver—the type of `self`. Given the receiver +> and name of a method, Rust can figure out definitively whether the method is +> reading (`&self`), mutating (`&mut self`), or consuming (`self`). The fact +> that Rust makes borrowing implicit for method receivers is a big part of +> making ownership ergonomic in practice. + +### Methods with More Parameters + +Let’s practice using methods by implementing a second method on the `Rectangle` +struct. This time, we want an instance of `Rectangle` to take another instance +of `Rectangle` and return `true` if the second `Rectangle` can fit completely +within `self` (the first `Rectangle`); otherwise it should return `false`. That +is, once we’ve defined the `can_hold` method, we want to be able to write the +program shown in Listing 5-14. + +Filename: src/main.rs + +``` +fn main() { + let rect1 = Rectangle { + width: 30, + height: 50, + }; + let rect2 = Rectangle { + width: 10, + height: 40, + }; + let rect3 = Rectangle { + width: 60, + height: 45, + }; + + println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2)); + println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3)); +} +``` + +Listing 5-14: Using the as-yet-unwritten `can_hold` method + +And the expected output would look like the following, because both dimensions +of `rect2` are smaller than the dimensions of `rect1` but `rect3` is wider than +`rect1`: + +``` +Can rect1 hold rect2? true +Can rect1 hold rect3? false +``` + +We know we want to define a method, so it will be within the `impl Rectangle` +block. The method name will be `can_hold`, and it will take an immutable borrow +of another `Rectangle` as a parameter. We can tell what the type of the +parameter will be by looking at the code that calls the method: +`rect1.can_hold(&rect2)` passes in `&rect2`, which is an immutable borrow to +`rect2`, an instance of `Rectangle`. This makes sense because we only need to +read `rect2` (rather than write, which would mean we’d need a mutable borrow), +and we want `main` to retain ownership of `rect2` so we can use it again after +calling the `can_hold` method. The return value of `can_hold` will be a +Boolean, and the implementation will check whether the width and height of +`self` are both greater than the width and height of the other `Rectangle`, +respectively. Let’s add the new `can_hold` method to the `impl` block from +Listing 5-13, shown in Listing 5-15. + +Filename: src/main.rs + +``` +impl Rectangle { + fn area(&self) -> u32 { + self.width * self.height + } + + fn can_hold(&self, other: &Rectangle) -> bool { + self.width > other.width && self.height > other.height + } +} +``` + +Listing 5-15: Implementing the `can_hold` method on `Rectangle` that takes +another `Rectangle` instance as a parameter + +When we run this code with the `main` function in Listing 5-14, we’ll get our +desired output. Methods can take multiple parameters that we add to the +signature after the `self` parameter, and those parameters work just like +parameters in functions. + +### Associated Functions + +All functions defined within an `impl` block are called *associated functions* +because they’re associated with the type named after the `impl`. We can define +associated functions that don’t have `self` as their first parameter (and thus +are not methods) because they don’t need an instance of the type to work with. +We’ve already used one function like this: the `String::from` function that’s +defined on the `String` type. + +Associated functions that aren’t methods are often used for constructors that +will return a new instance of the struct. These are often called `new`, but +`new` isn’t a special name and isn’t built into the language. For example, we +could choose to provide an associated function named `square` that would have +one dimension parameter and use that as both width and height, thus making it +easier to create a square `Rectangle` rather than having to specify the same +value twice: + +Filename: src/main.rs + +``` +impl Rectangle { + fn square(size: u32) -> Self [1] { + Self [2] { + width: size, + height: size, + } + } +} +``` + +The `Self` keywords in the return type [1] and in the body of the function [2] +are aliases for the type that appears after the `impl` keyword, which in this +case is `Rectangle`. + +To call this associated function, we use the `::` syntax with the struct name; +`let sq = Rectangle::square(3);` is an example. This function is namespaced by +the struct: the `::` syntax is used for both associated functions and +namespaces created by modules. We’ll discuss modules in Chapter 7. + +<!--- Should we mention the most common associated function is `new`? And that +new isn't built into the language. +/JT ---> +<!-- I've added a note as such above to the paragraph that starts with +"Associated functions that aren’t methods" /Carol --> + +### Multiple `impl` Blocks + +Each struct is allowed to have multiple `impl` blocks. For example, Listing +5-15 is equivalent to the code shown in Listing 5-16, which has each method +in its own `impl` block. + +``` +impl Rectangle { + fn area(&self) -> u32 { + self.width * self.height + } +} + +impl Rectangle { + fn can_hold(&self, other: &Rectangle) -> bool { + self.width > other.width && self.height > other.height + } +} +``` + +Listing 5-16: Rewriting Listing 5-15 using multiple `impl` blocks + +There’s no reason to separate these methods into multiple `impl` blocks here, +but this is valid syntax. We’ll see a case in which multiple `impl` blocks are +useful in Chapter 10, where we discuss generic types and traits. + +## Summary + +Structs let you create custom types that are meaningful for your domain. By +using structs, you can keep associated pieces of data connected to each other +and name each piece to make your code clear. In `impl` blocks, you can define +functions that are associated with your type, and methods are a kind of +associated function that let you specify the behavior that instances of your +structs have. + +But structs aren’t the only way you can create custom types: let’s turn to +Rust’s enum feature to add another tool to your toolbox. + +<!--- We don't mention that you can only use `impl` in the same crate as the +type it's created in, otherwise you could use `impl` and add methods on types +that come from other people (which you can't do, unless you make a trait to +attach them to) + +Another thing we may want to mention is that `Self` inside of an `impl` refers +to the type being impl'd. So you might write the above: + +``` +impl Rectangle { + fn square(size: u32) -> Self { + Self { + width: size, + height: size, + } + } +} +``` +which is often a bit more ergonomic. + +/JT ---> +<!-- I've changed the `square` example to use `Self` and added some wingdings +and notes explaining that. I don't really want to get into the restrictions on +`impl` on types defined in another crate, because we haven't covered traits +yet. Traits let you do `impl Trait for OtherCrateType` in some circumstances, +so I don't want to say "you can't use an `impl` block on types from other +crates" because I'd have to allude to traits or potentially give the reader the +wrong impression. We get into these restrictions in chapter 10. --> |