2022-09-09 23:59:35 +00:00
|
|
|
|
<!-- 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/`.
|
|
|
|
|
-->
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
[TOC]
|
|
|
|
|
|
|
|
|
|
# Using Structs to Structure Related Data
|
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
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
|
2021-08-02 01:30:39 +00:00
|
|
|
|
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
|
2021-12-22 22:40:46 +00:00
|
|
|
|
with structs to build on what you already know and demonstrate when structs are
|
|
|
|
|
a better way to group data.
|
2022-05-21 02:17:40 +00:00
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
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
|
2022-08-15 16:10:37 +00:00
|
|
|
|
program’s domain to take full advantage of Rust’s compile-time type checking.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
## Defining and Instantiating Structs
|
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
Structs are similar to tuples, discussed in “The Tuple Type” on page XX, 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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
struct User {
|
|
|
|
|
active: bool,
|
|
|
|
|
username: String,
|
|
|
|
|
email: String,
|
|
|
|
|
sign_in_count: u64,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-1: A `User` struct definition
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
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
|
2022-08-16 00:30:44 +00:00
|
|
|
|
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
|
2021-08-02 01:30:39 +00:00
|
|
|
|
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.
|
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
2021-12-22 22:41:20 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let user1 = User {
|
|
|
|
|
active: true,
|
2022-08-15 16:10:37 +00:00
|
|
|
|
username: String::from("someusername123"),
|
|
|
|
|
email: String::from("someone@example.com"),
|
2021-12-22 22:41:20 +00:00
|
|
|
|
sign_in_count: 1,
|
|
|
|
|
};
|
|
|
|
|
}
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-2: Creating an instance of the `User` struct
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-05-21 13:58:41 +00:00
|
|
|
|
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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
2021-12-22 22:41:20 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let mut user1 = User {
|
|
|
|
|
active: true,
|
2022-08-15 16:10:37 +00:00
|
|
|
|
username: String::from("someusername123"),
|
|
|
|
|
email: String::from("someone@example.com"),
|
2021-12-22 22:41:20 +00:00
|
|
|
|
sign_in_count: 1,
|
|
|
|
|
};
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-12-22 22:41:20 +00:00
|
|
|
|
user1.email = String::from("anotheremail@example.com");
|
|
|
|
|
}
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-3: Changing the value in the `email` field of a `User` instance
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
active: true,
|
2022-08-15 16:10:37 +00:00
|
|
|
|
username: username,
|
|
|
|
|
email: email,
|
2021-08-02 01:30:39 +00:00
|
|
|
|
sign_in_count: 1,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-4: A `build_user` function that takes an email and username and
|
|
|
|
|
returns a `User` instance
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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!
|
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
### Using the Field Init Shorthand
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2022-08-15 16:10:37 +00:00
|
|
|
|
`build_user` so it behaves exactly the same but doesn’t have the repetition of
|
2022-09-09 23:59:35 +00:00
|
|
|
|
`username` and `email`, as shown in Listing 5-5.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
fn build_user(email: String, username: String) -> User {
|
|
|
|
|
User {
|
|
|
|
|
active: true,
|
2022-08-15 16:10:37 +00:00
|
|
|
|
username,
|
|
|
|
|
email,
|
2021-08-02 01:30:39 +00:00
|
|
|
|
sign_in_count: 1,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-5: A `build_user` function that uses field init shorthand because the
|
|
|
|
|
`username` and `email` parameters have the same name as struct fields
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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`.
|
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
### Creating Instances from Other Instances with Struct Update Syntax
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
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*.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
2021-12-22 22:41:20 +00:00
|
|
|
|
fn main() {
|
2022-08-15 16:10:37 +00:00
|
|
|
|
--snip--
|
|
|
|
|
|
2021-12-22 22:41:20 +00:00
|
|
|
|
let user2 = User {
|
|
|
|
|
active: user1.active,
|
|
|
|
|
username: user1.username,
|
|
|
|
|
email: String::from("another@example.com"),
|
|
|
|
|
sign_in_count: user1.sign_in_count,
|
|
|
|
|
};
|
|
|
|
|
}
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-6: Creating a new `User` instance using one of the values from `user1`
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
2021-12-22 22:41:20 +00:00
|
|
|
|
fn main() {
|
2022-08-15 16:10:37 +00:00
|
|
|
|
--snip--
|
|
|
|
|
|
|
|
|
|
|
2021-12-22 22:41:20 +00:00
|
|
|
|
let user2 = User {
|
|
|
|
|
email: String::from("another@example.com"),
|
|
|
|
|
..user1
|
|
|
|
|
};
|
|
|
|
|
}
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-7: Using struct update syntax to set a new `email` value for a `User`
|
|
|
|
|
instance but to use the rest of the values from `user1`
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
Note that the struct update syntax uses `=` like an assignment; this is because
|
2022-08-16 00:30:44 +00:00
|
|
|
|
it moves the data, just as we saw in “Variables and Data Interacting with Move”
|
2022-08-15 16:10:37 +00:00
|
|
|
|
on page XX. 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`. Both
|
|
|
|
|
`active` and `sign_in_count` are types that implement the `Copy` trait, so the
|
|
|
|
|
behavior we discussed in “Stack-Only Data: Copy” on page XX would apply.
|
|
|
|
|
|
|
|
|
|
### Using Tuple Structs Without Named Fields to Create Different Types
|
|
|
|
|
|
2022-09-09 23:59:35 +00:00
|
|
|
|
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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
To define a tuple struct, start with the `struct` keyword and the struct name
|
2022-08-15 16:10:37 +00:00
|
|
|
|
followed by the types in the tuple. For example, here we define and use two
|
|
|
|
|
tuple structs named `Color` and `Point`:
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
struct Color(i32, i32, i32);
|
|
|
|
|
struct Point(i32, i32, i32);
|
|
|
|
|
|
2021-12-22 22:41:20 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let black = Color(0, 0, 0);
|
|
|
|
|
let origin = Point(0, 0, 0);
|
|
|
|
|
}
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
Note that the `black` and `origin` values are different types because they’re
|
2021-08-02 01:30:39 +00:00
|
|
|
|
instances of different tuple structs. Each struct you define is its own type,
|
2022-05-21 13:58:41 +00:00
|
|
|
|
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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
### 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
|
2022-08-15 16:10:37 +00:00
|
|
|
|
we mentioned in “The Tuple Type” on page XX. 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`:
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
struct AlwaysEqual;
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-12-22 22:41:20 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let subject = AlwaysEqual;
|
|
|
|
|
}
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
To define `AlwaysEqual`, we use the `struct` keyword, the name we want, and
|
|
|
|
|
then a semicolon. No need for curly brackets or parentheses! Then we can get an
|
2021-08-02 01:30:39 +00:00
|
|
|
|
instance of `AlwaysEqual` in the `subject` variable in a similar way: using the
|
2021-12-22 22:40:46 +00:00
|
|
|
|
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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
> ### Ownership of Struct Data
|
2022-09-09 23:59:35 +00:00
|
|
|
|
>
|
2021-08-02 01:30:39 +00:00
|
|
|
|
> In the `User` struct definition in Listing 5-1, we used the owned `String`
|
2022-08-15 16:10:37 +00:00
|
|
|
|
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.
|
2022-09-09 23:59:35 +00:00
|
|
|
|
>
|
2021-12-22 22:40:46 +00:00
|
|
|
|
> It’s also possible for structs to store references to data owned by something
|
2022-08-15 16:10:37 +00:00
|
|
|
|
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 in *src/main.rs*; this
|
|
|
|
|
won’t work:
|
2022-09-13 14:54:59 +00:00
|
|
|
|
>
|
|
|
|
|
> ```
|
|
|
|
|
> struct User {
|
|
|
|
|
> active: bool,
|
|
|
|
|
> username: &str,
|
|
|
|
|
> email: &str,
|
|
|
|
|
> sign_in_count: u64,
|
|
|
|
|
> }
|
|
|
|
|
>
|
|
|
|
|
> fn main() {
|
|
|
|
|
> let user1 = User {
|
|
|
|
|
> active: true,
|
|
|
|
|
> username: "someusername123",
|
|
|
|
|
> email: "someone@example.com",
|
|
|
|
|
> 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
|
2022-08-15 16:10:37 +00:00
|
|
|
|
references in structs, but for now, we’ll fix errors like these using owned
|
|
|
|
|
types like `String` instead of references like `&str`.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
## An Example Program Using Structs
|
|
|
|
|
|
|
|
|
|
To understand when we might want to use structs, let’s write a program that
|
2021-12-22 22:40:46 +00:00
|
|
|
|
calculates the area of a rectangle. We’ll start by using single variables, and
|
|
|
|
|
then refactor the program until we’re using structs instead.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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;
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
println!(
|
|
|
|
|
"The area of the rectangle is {} square pixels.",
|
|
|
|
|
area(width1, height1)
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
fn area(width: u32, height: u32) -> u32 {
|
|
|
|
|
width * height
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-8: Calculating the area of a rectangle specified by separate width
|
|
|
|
|
and height variables
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
Now, run this program using `cargo run`:
|
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
```
|
2021-08-02 01:30:39 +00:00
|
|
|
|
The area of the rectangle is 1500 square pixels.
|
|
|
|
|
```
|
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2022-08-15 16:10:37 +00:00
|
|
|
|
function we wrote has two parameters, and it’s not clear anywhere in our
|
2021-12-22 22:40:46 +00:00
|
|
|
|
program that the parameters are related. It would be more readable and more
|
2021-08-02 01:30:39 +00:00
|
|
|
|
manageable to group width and height together. We’ve already discussed one way
|
2022-08-15 16:10:37 +00:00
|
|
|
|
we might do that in “The Tuple Type” on page XX: by using tuples.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
### 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);
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
println!(
|
|
|
|
|
"The area of the rectangle is {} square pixels.",
|
2022-08-16 00:30:44 +00:00
|
|
|
|
1 area(rect1)
|
2021-08-02 01:30:39 +00:00
|
|
|
|
);
|
2022-09-09 23:59:35 +00:00
|
|
|
|
}
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2022-09-09 23:59:35 +00:00
|
|
|
|
fn area(dimensions: (u32, u32)) -> u32 {
|
|
|
|
|
2 dimensions.0 * dimensions.1
|
2021-08-02 01:30:39 +00:00
|
|
|
|
}
|
2022-08-15 16:10:37 +00:00
|
|
|
|
```
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-9: Specifying the width and height of the rectangle with a tuple
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
In one way, this program is better. Tuples let us add a bit of structure, and
|
2022-08-16 00:30:44 +00:00
|
|
|
|
we’re now passing just one argument [1]. 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 [2], making our calculation less obvious.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
### Refactoring with Structs: Adding More Meaning
|
|
|
|
|
|
|
|
|
|
We use structs to add meaning by labeling the data. We can transform the tuple
|
2021-12-22 22:40:46 +00:00
|
|
|
|
we’re using into a struct with a name for the whole as well as names for the
|
2021-08-02 01:30:39 +00:00
|
|
|
|
parts, as shown in Listing 5-10.
|
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
2022-08-16 00:30:44 +00:00
|
|
|
|
1 struct Rectangle {
|
|
|
|
|
2 width: u32,
|
2021-08-02 01:30:39 +00:00
|
|
|
|
height: u32,
|
|
|
|
|
}
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
fn main() {
|
2022-08-16 00:30:44 +00:00
|
|
|
|
3 let rect1 = Rectangle {
|
2021-08-02 01:30:39 +00:00
|
|
|
|
width: 30,
|
|
|
|
|
height: 50,
|
|
|
|
|
};
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
println!(
|
|
|
|
|
"The area of the rectangle is {} square pixels.",
|
|
|
|
|
area(&rect1)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
4 fn area(rectangle: &Rectangle) -> u32 {
|
|
|
|
|
5 rectangle.width * rectangle.height
|
2021-08-02 01:30:39 +00:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-10: Defining a `Rectangle` struct
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
Here, we’ve defined a struct and named it `Rectangle` [1]. Inside the curly
|
2021-08-02 01:30:39 +00:00
|
|
|
|
brackets, we defined the fields as `width` and `height`, both of which have
|
2022-08-24 13:12:29 +00:00
|
|
|
|
type `u32` [2]. Then, in `main`, we created a particular instance of
|
|
|
|
|
`Rectangle` that has a width of `30` and a height of `50` [3].
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
Our `area` function is now defined with one parameter, which we’ve named
|
2022-08-16 00:30:44 +00:00
|
|
|
|
`rectangle`, whose type is an immutable borrow of a struct `Rectangle` instance
|
|
|
|
|
[4]. 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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
The `area` function accesses the `width` and `height` fields of the `Rectangle`
|
2022-08-16 00:30:44 +00:00
|
|
|
|
instance [5] (note that accessing fields of a borrowed struct instance does not
|
2022-05-21 13:58:41 +00:00
|
|
|
|
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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
### Adding Useful Functionality with Derived Traits
|
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
It’d be useful to be able to print an instance of `Rectangle` while we’re
|
2021-08-02 01:30:39 +00:00
|
|
|
|
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,
|
|
|
|
|
};
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
println!("rect1 is {}", rect1);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-11: Attempting to print a `Rectangle` instance
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2022-08-15 16:10:37 +00:00
|
|
|
|
implement `Display` by default because there’s only one way you’d want to show
|
2021-08-02 01:30:39 +00:00
|
|
|
|
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
|
2021-12-22 22:40:46 +00:00
|
|
|
|
implementation of `Display` to use with `println!` and the `{}` placeholder.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
If we continue reading the errors, we’ll find this helpful note:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
= help: the trait `std::fmt::Display` is not implemented for `Rectangle`
|
2022-08-15 16:10:37 +00:00
|
|
|
|
= note: in format strings you may be able to use `{:?}` (or {:#?} for
|
|
|
|
|
pretty-print) instead
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
}
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let rect1 = Rectangle {
|
|
|
|
|
width: 30,
|
|
|
|
|
height: 50,
|
|
|
|
|
};
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
println!("rect1 is {:?}", rect1);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-12: Adding the attribute to derive the `Debug` trait and printing the
|
|
|
|
|
`Rectangle` instance using debug formatting
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
Now when we run the program, we won’t get any errors, and we’ll see the
|
|
|
|
|
following output:
|
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
```
|
2021-08-02 01:30:39 +00:00
|
|
|
|
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
|
2022-08-15 16:10:37 +00:00
|
|
|
|
those cases, we can use `{:#?}` instead of `{:?}` in the `println!` string. In
|
|
|
|
|
this example, using the `{:#?}` style will output the following:
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
```
|
2021-08-02 01:30:39 +00:00
|
|
|
|
rect1 is Rectangle {
|
|
|
|
|
width: 30,
|
|
|
|
|
height: 50,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
Another way to print out a value using the `Debug` format is to use the `dbg!`
|
2022-08-15 16:10:37 +00:00
|
|
|
|
macro, which takes ownership of an expression (as opposed to `println!`, which
|
2022-05-21 13:58:41 +00:00
|
|
|
|
takes a reference), prints the file and line number of where that `dbg!` macro
|
2022-08-15 16:10:37 +00:00
|
|
|
|
call occurs in your code along with the resultant value of that expression, and
|
2022-05-21 13:58:41 +00:00
|
|
|
|
returns ownership of the value.
|
|
|
|
|
|
2022-09-09 23:59:35 +00:00
|
|
|
|
> Note: Calling the `dbg!` macro prints to the standard error console stream
|
2022-08-24 13:12:29 +00:00
|
|
|
|
(`stderr`), as opposed to `println!`, which prints to the standard output
|
2022-08-15 16:10:37 +00:00
|
|
|
|
console stream (`stdout`). We’ll talk more about `stderr` and `stdout` in
|
|
|
|
|
“Writing Error Messages to Standard Error Instead of Standard Output” on page
|
|
|
|
|
XX.
|
2021-12-22 22:40:46 +00:00
|
|
|
|
|
|
|
|
|
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`:
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
```
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct Rectangle {
|
|
|
|
|
width: u32,
|
|
|
|
|
height: u32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let scale = 2;
|
|
|
|
|
let rect1 = Rectangle {
|
2022-08-16 00:30:44 +00:00
|
|
|
|
1 width: dbg!(30 * scale),
|
2021-08-02 01:30:39 +00:00
|
|
|
|
height: 50,
|
|
|
|
|
};
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
2 dbg!(&rect1);
|
2021-08-02 01:30:39 +00:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
We can put `dbg!` around the expression `30 * scale` [1] and, because `dbg!`
|
2021-08-02 01:30:39 +00:00
|
|
|
|
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
|
2022-08-16 00:30:44 +00:00
|
|
|
|
take ownership of `rect1`, so we use a reference to `rect1` in the next call
|
|
|
|
|
[2]. Here’s what the output of this example looks like:
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
|
|
|
|
```
|
2021-08-02 01:30:39 +00:00
|
|
|
|
[src/main.rs:10] 30 * scale = 60
|
|
|
|
|
[src/main.rs:14] &rect1 = Rectangle {
|
|
|
|
|
width: 60,
|
|
|
|
|
height: 50,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
We can see the first bit of output came from [1] where we’re debugging the
|
2022-08-24 13:12:29 +00:00
|
|
|
|
expression `30 * scale`, and its resultant value is `60` (the `Debug`
|
|
|
|
|
formatting implemented for integers is to print only their value). The `dbg!`
|
|
|
|
|
call at [2] 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!
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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.
|
2022-08-15 16:10:37 +00:00
|
|
|
|
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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
## Method Syntax
|
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
*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
|
2022-08-15 16:10:37 +00:00
|
|
|
|
object, which we cover in Chapter 6 and Chapter 17, respectively), and their
|
|
|
|
|
first parameter is always `self`, which represents the instance of the struct
|
|
|
|
|
the method is being called on.
|
2022-05-21 02:17:40 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
### 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
|
|
|
|
|
|
|
|
|
|
```
|
2022-08-15 16:10:37 +00:00
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct Rectangle {
|
|
|
|
|
width: u32,
|
2021-08-02 01:30:39 +00:00
|
|
|
|
height: u32,
|
|
|
|
|
}
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2022-08-16 00:30:44 +00:00
|
|
|
|
1 impl Rectangle {
|
|
|
|
|
2 fn area(&self) -> u32 {
|
2021-08-02 01:30:39 +00:00
|
|
|
|
self.width * self.height
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let rect1 = Rectangle {
|
|
|
|
|
width: 30,
|
|
|
|
|
height: 50,
|
|
|
|
|
};
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
println!(
|
|
|
|
|
"The area of the rectangle is {} square pixels.",
|
2022-08-16 00:30:44 +00:00
|
|
|
|
3 rect1.area()
|
2021-08-02 01:30:39 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-13: Defining an `area` method on the `Rectangle` struct
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
To define the function within the context of `Rectangle`, we start an `impl`
|
2022-08-16 00:30:44 +00:00
|
|
|
|
(implementation) block for `Rectangle` [1]. Everything within this `impl` block
|
2021-08-02 01:30:39 +00:00
|
|
|
|
will be associated with the `Rectangle` type. Then we move the `area` function
|
2022-08-16 00:30:44 +00:00
|
|
|
|
within the `impl` curly brackets [2] 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 [3]. The method syntax goes after an instance: we add a
|
|
|
|
|
dot followed by the method name, parentheses, and any arguments.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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
|
2022-08-24 13:12:29 +00:00
|
|
|
|
indicate that 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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
We chose `&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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
Note that we can choose to give a method the same name as one of the struct’s
|
2022-08-24 13:12:29 +00:00
|
|
|
|
fields. For example, we can define a method on `Rectangle` that is also named
|
|
|
|
|
`width`:
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
impl Rectangle {
|
|
|
|
|
fn width(&self) -> bool {
|
|
|
|
|
self.width > 0
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
fn main() {
|
|
|
|
|
let rect1 = Rectangle {
|
|
|
|
|
width: 30,
|
|
|
|
|
height: 50,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if rect1.width() {
|
2022-08-16 00:30:44 +00:00
|
|
|
|
println!(
|
|
|
|
|
"The rectangle has a nonzero width; it is {}",
|
|
|
|
|
rect1.width
|
|
|
|
|
);
|
2021-08-02 01:30:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
Here, we’re choosing to make the `width` method return `true` if the value in
|
2022-08-24 13:12:29 +00:00
|
|
|
|
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`.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2021-12-22 22:40:46 +00:00
|
|
|
|
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
|
2022-08-24 13:12:29 +00:00
|
|
|
|
make the field private but the method public, and thus enable read-only access
|
2022-08-15 16:10:37 +00:00
|
|
|
|
to that field as part of the type’s public API. We will discuss what public and
|
|
|
|
|
private are and how to designate a field or method as public or private in
|
|
|
|
|
Chapter 7.
|
|
|
|
|
|
|
|
|
|
> ### Where’s the -> Operator?
|
2022-09-09 23:59:35 +00:00
|
|
|
|
>
|
2021-08-02 01:30:39 +00:00
|
|
|
|
> In C and C++, two different operators are used for calling methods: you use
|
2022-08-15 16:10:37 +00:00
|
|
|
|
`.` 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
|
2022-08-24 13:12:29 +00:00
|
|
|
|
pointer first. In other words, if `object` is a pointer,
|
|
|
|
|
`object->`something`()` is similar to `(*object).`something`()`.
|
2022-09-09 23:59:35 +00:00
|
|
|
|
>
|
2021-08-02 01:30:39 +00:00
|
|
|
|
> Rust doesn’t have an equivalent to the `->` operator; instead, Rust has a
|
2022-08-15 16:10:37 +00:00
|
|
|
|
feature called *automatic referencing and dereferencing*. Calling methods is
|
|
|
|
|
one of the few places in Rust that has this behavior.
|
2022-09-09 23:59:35 +00:00
|
|
|
|
>
|
2022-08-24 13:12:29 +00:00
|
|
|
|
> Here’s how it works: when you call a method with `object.`something`()`, Rust
|
2022-08-15 16:10:37 +00:00
|
|
|
|
automatically adds in `&`, `&mut`, or `*` so `object` matches the signature of
|
|
|
|
|
the method. In other words, the following are the same:
|
2022-09-13 14:54:59 +00:00
|
|
|
|
>
|
|
|
|
|
> ```
|
|
|
|
|
> 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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
### Methods with More Parameters
|
|
|
|
|
|
|
|
|
|
Let’s practice using methods by implementing a second method on the `Rectangle`
|
2022-08-15 16:10:37 +00:00
|
|
|
|
struct. This time we want an instance of `Rectangle` to take another instance
|
2021-08-02 01:30:39 +00:00
|
|
|
|
of `Rectangle` and return `true` if the second `Rectangle` can fit completely
|
2022-08-24 13:12:29 +00:00
|
|
|
|
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.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
};
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
|
|
|
|
|
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-14: Using the as-yet-unwritten `can_hold` method
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
The expected output would look like the following because both dimensions of
|
2022-08-24 13:12:29 +00:00
|
|
|
|
`rect2` are smaller than the dimensions of `rect1`, but `rect3` is wider than
|
2021-08-02 01:30:39 +00:00
|
|
|
|
`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
|
2022-08-15 16:10:37 +00:00
|
|
|
|
`self` are greater than the width and height of the other `Rectangle`,
|
2021-08-02 01:30:39 +00:00
|
|
|
|
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
|
|
|
|
|
}
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
fn can_hold(&self, other: &Rectangle) -> bool {
|
|
|
|
|
self.width > other.width && self.height > other.height
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-15: Implementing the `can_hold` method on `Rectangle` that takes
|
|
|
|
|
another `Rectangle` instance as a parameter
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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.
|
2021-12-22 22:40:46 +00:00
|
|
|
|
We’ve already used one function like this: the `String::from` function that’s
|
2021-08-02 01:30:39 +00:00
|
|
|
|
defined on the `String` type.
|
|
|
|
|
|
|
|
|
|
Associated functions that aren’t methods are often used for constructors that
|
2022-05-21 13:58:41 +00:00
|
|
|
|
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:
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
Filename: src/main.rs
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
impl Rectangle {
|
2022-08-15 16:10:37 +00:00
|
|
|
|
fn square(size: u32) -> 1 Self {
|
|
|
|
|
2 Self {
|
2021-08-02 01:30:39 +00:00
|
|
|
|
width: size,
|
|
|
|
|
height: size,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-05-21 13:58:41 +00:00
|
|
|
|
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`.
|
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
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.
|
|
|
|
|
|
2022-08-15 16:10:37 +00:00
|
|
|
|
### Multiple impl Blocks
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
Each struct is allowed to have multiple `impl` blocks. For example, Listing
|
2022-08-15 16:10:37 +00:00
|
|
|
|
5-15 is equivalent to the code shown in Listing 5-16, which has each method in
|
|
|
|
|
its own `impl` block.
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
impl Rectangle {
|
|
|
|
|
fn area(&self) -> u32 {
|
|
|
|
|
self.width * self.height
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-15 16:10:37 +00:00
|
|
|
|
|
2021-08-02 01:30:39 +00:00
|
|
|
|
impl Rectangle {
|
|
|
|
|
fn can_hold(&self, other: &Rectangle) -> bool {
|
|
|
|
|
self.width > other.width && self.height > other.height
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 5-16: Rewriting Listing 5-15 using multiple `impl` blocks
|
2021-08-02 01:30:39 +00:00
|
|
|
|
|
|
|
|
|
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.
|
2022-05-21 02:17:40 +00:00
|
|
|
|
|