Files
docs-rust/ch05/ch05-01-defining-structs.html
2026-06-22 21:27:36 +05:30

380 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Defining and Instantiating Structs</title>
</head>
<body>
<h2 id="defining-and-instantiating-structs"><a class="header" href="#defining-and-instantiating-structs">Defining and Instantiating Structs</a></h2>
<p>Structs are similar to tuples, discussed in <a href="../ch03/ch03-02-data-types.html#the-tuple-type">“The Tuple Type”</a><!--
ignore --> 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
youll name each piece of data so its clear what the values mean. Adding these
names means that structs are more flexible than tuples: You dont have to rely
on the order of the data to specify or access the values of an instance.</p>
<p>To define a struct, we enter the keyword <code>struct</code> and name the entire struct. A
structs 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 <em>fields</em>. For example, Listing 5-1 shows a
struct that stores information about a user account.</p>
<figure class="listing" id="listing-5-1">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024">struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
<span class="boring">
</span><span class="boring">fn main() {}</span></code></pre>
<figcaption><a href="#listing-5-1">Listing 5-1</a>: A <code>User</code> struct definition</figcaption>
</figure>
<p>To use a struct after weve defined it, we create an <em>instance</em> 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 <em><code>key: value</code></em> pairs, where the keys are the names of the fields and the values are the
data we want to store in those fields. We dont 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.</p>
<figure class="listing" id="listing-5-2">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">struct User {
</span><span class="boring"> active: bool,
</span><span class="boring"> username: String,
</span><span class="boring"> email: String,
</span><span class="boring"> sign_in_count: u64,
</span><span class="boring">}
</span><span class="boring">
</span>fn main() {
let user1 = User {
active: true,
username: String::from("someusername123"),
email: String::from("someone@example.com"),
sign_in_count: 1,
};
}</code></pre>
<figcaption><a href="#listing-5-2">Listing 5-2</a>: Creating an instance of the <code>User</code> struct</figcaption>
</figure>
<p>To get a specific value from a struct, we use dot notation. For example, to
access this users email address, we use <code>user1.email</code>. 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 <code>email</code>
field of a mutable <code>User</code> instance.</p>
<figure class="listing" id="listing-5-3">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">struct User {
</span><span class="boring"> active: bool,
</span><span class="boring"> username: String,
</span><span class="boring"> email: String,
</span><span class="boring"> sign_in_count: u64,
</span><span class="boring">}
</span><span class="boring">
</span>fn main() {
let mut user1 = User {
active: true,
username: String::from("someusername123"),
email: String::from("someone@example.com"),
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
}</code></pre>
<figcaption><a href="#listing-5-3">Listing 5-3</a>: Changing the value in the <code>email</code> field of a <code>User</code> instance</figcaption>
</figure>
<p>Note that the entire instance must be mutable; Rust doesnt 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.</p>
<p>Listing 5-4 shows a <code>build_user</code> function that returns a <code>User</code> instance with
the given email and username. The <code>active</code> field gets the value <code>true</code>, and the
<code>sign_in_count</code> gets a value of <code>1</code>.</p>
<figure class="listing" id="listing-5-4">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">struct User {
</span><span class="boring"> active: bool,
</span><span class="boring"> username: String,
</span><span class="boring"> email: String,
</span><span class="boring"> sign_in_count: u64,
</span><span class="boring">}
</span><span class="boring">
</span>fn build_user(email: String, username: String) -&gt; User {
User {
active: true,
username: username,
email: email,
sign_in_count: 1,
}
}
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let user1 = build_user(
</span><span class="boring"> String::from("someone@example.com"),
</span><span class="boring"> String::from("someusername123"),
</span><span class="boring"> );
</span><span class="boring">}</span></code></pre>
<figcaption><a href="#listing-5-4">Listing 5-4</a>: A <code>build_user</code> function that takes an email and username and returns a <code>User</code> instance</figcaption>
</figure>
<p>It makes sense to name the function parameters with the same name as the struct
fields, but having to repeat the <code>email</code> and <code>username</code> field names and
variables is a bit tedious. If the struct had more fields, repeating each name
would get even more annoying. Luckily, theres a convenient shorthand!</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="using-the-field-init-shorthand-when-variables-and-fields-have-the-same-name"></a></p>
<h3 id="using-the-field-init-shorthand"><a class="header" href="#using-the-field-init-shorthand">Using the Field Init Shorthand</a></h3>
<p>Because the parameter names and the struct field names are exactly the same in
Listing 5-4, we can use the <em>field init shorthand</em> syntax to rewrite
<code>build_user</code> so that it behaves exactly the same but doesnt have the
repetition of <code>username</code> and <code>email</code>, as shown in Listing 5-5.</p>
<figure class="listing" id="listing-5-5">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">struct User {
</span><span class="boring"> active: bool,
</span><span class="boring"> username: String,
</span><span class="boring"> email: String,
</span><span class="boring"> sign_in_count: u64,
</span><span class="boring">}
</span><span class="boring">
</span>fn build_user(email: String, username: String) -&gt; User {
User {
active: true,
username,
email,
sign_in_count: 1,
}
}
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let user1 = build_user(
</span><span class="boring"> String::from("someone@example.com"),
</span><span class="boring"> String::from("someusername123"),
</span><span class="boring"> );
</span><span class="boring">}</span></code></pre>
<figcaption><a href="#listing-5-5">Listing 5-5</a>: A <code>build_user</code> function that uses field init shorthand because the <code>username</code> and <code>email</code> parameters have the same name as struct fields</figcaption>
</figure>
<p>Here, were creating a new instance of the <code>User</code> struct, which has a field
named <code>email</code>. We want to set the <code>email</code> fields value to the value in the
<code>email</code> parameter of the <code>build_user</code> function. Because the <code>email</code> field and
the <code>email</code> parameter have the same name, we only need to write <code>email</code> rather
than <code>email: email</code>.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="creating-instances-from-other-instances-with-struct-update-syntax"></a></p>
<h3 id="creating-instances-with-struct-update-syntax"><a class="header" href="#creating-instances-with-struct-update-syntax">Creating Instances with Struct Update Syntax</a></h3>
<p>Its often useful to create a new instance of a struct that includes most of
the values from another instance of the same type, but changes some of them.
You can do this using struct update syntax.</p>
<p>First, in Listing 5-6 we show how to create a new <code>User</code> instance in <code>user2</code> in
the regular way, without the update syntax. We set a new value for <code>email</code> but
otherwise use the same values from <code>user1</code> that we created in Listing 5-2.</p>
<figure class="listing" id="listing-5-6">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">struct User {
</span><span class="boring"> active: bool,
</span><span class="boring"> username: String,
</span><span class="boring"> email: String,
</span><span class="boring"> sign_in_count: u64,
</span><span class="boring">}
</span><span class="boring">
</span>fn main() {
// --snip--
<span class="boring">
</span><span class="boring"> let user1 = User {
</span><span class="boring"> email: String::from("someone@example.com"),
</span><span class="boring"> username: String::from("someusername123"),
</span><span class="boring"> active: true,
</span><span class="boring"> sign_in_count: 1,
</span><span class="boring"> };
</span>
let user2 = User {
active: user1.active,
username: user1.username,
email: String::from("another@example.com"),
sign_in_count: user1.sign_in_count,
};
}</code></pre>
<figcaption><a href="#listing-5-6">Listing 5-6</a>: Creating a new <code>User</code> instance using all but one of the values from <code>user1</code></figcaption>
</figure>
<p>Using struct update syntax, we can achieve the same effect with less code, as
shown in Listing 5-7. The syntax <code>..</code> specifies that the remaining fields not
explicitly set should have the same value as the fields in the given instance.</p>
<figure class="listing" id="listing-5-7">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">struct User {
</span><span class="boring"> active: bool,
</span><span class="boring"> username: String,
</span><span class="boring"> email: String,
</span><span class="boring"> sign_in_count: u64,
</span><span class="boring">}
</span><span class="boring">
</span>fn main() {
// --snip--
<span class="boring">
</span><span class="boring"> let user1 = User {
</span><span class="boring"> email: String::from("someone@example.com"),
</span><span class="boring"> username: String::from("someusername123"),
</span><span class="boring"> active: true,
</span><span class="boring"> sign_in_count: 1,
</span><span class="boring"> };
</span>
let user2 = User {
email: String::from("another@example.com"),
..user1
};
}</code></pre>
<figcaption><a href="#listing-5-7">Listing 5-7</a>: Using struct update syntax to set a new <code>email</code> value for a <code>User</code> instance but to use the rest of the values from <code>user1</code></figcaption>
</figure>
<p>The code in Listing 5-7 also creates an instance in <code>user2</code> that has a
different value for <code>email</code> but has the same values for the <code>username</code>,
<code>active</code>, and <code>sign_in_count</code> fields from <code>user1</code>. The <code>..user1</code> must come last
to specify that any remaining fields should get their values from the
corresponding fields in <code>user1</code>, 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 structs definition.</p>
<p>Note that the struct update syntax uses <code>=</code> like an assignment; this is because
it moves the data, just as we saw in the <a href="../ch04/ch04-01-what-is-ownership.html#variables-and-data-interacting-with-move">“Variables and Data Interacting with
Move”</a><!-- ignore --> section. In this example, we can no longer use
<code>user1</code> after creating <code>user2</code> because the <code>String</code> in the <code>username</code> field of
<code>user1</code> was moved into <code>user2</code>. If we had given <code>user2</code> new <code>String</code> values for
both <code>email</code> and <code>username</code>, and thus only used the <code>active</code> and <code>sign_in_count</code>
values from <code>user1</code>, then <code>user1</code> would still be valid after creating <code>user2</code>.
Both <code>active</code> and <code>sign_in_count</code> are types that implement the <code>Copy</code> trait, so
the behavior we discussed in the <a href="../ch04/ch04-01-what-is-ownership.html#stack-only-data-copy">“Stack-Only Data: Copy”</a><!-- ignore -->
section would apply. We can also still use <code>user1.email</code> in this example,
because its value was not moved out of <code>user1</code>.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="using-tuple-structs-without-named-fields-to-create-different-types"></a></p>
<h3 id="creating-different-types-with-tuple-structs"><a class="header" href="#creating-different-types-with-tuple-structs">Creating Different Types with Tuple Structs</a></h3>
<p>Rust also supports structs that look similar to tuples, called <em>tuple structs</em>.
Tuple structs have the added meaning the struct name provides but dont 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.</p>
<p>To define a tuple struct, start with the <code>struct</code> keyword and the struct name
followed by the types in the tuple. For example, here we define and use two
tuple structs named <code>Color</code> and <code>Point</code>:</p>
<figure class="listing">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024">struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}</code></pre>
</figure>
<p>Note that the <code>black</code> and <code>origin</code> values are different types because theyre
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 <code>Color</code> cannot take a
<code>Point</code> as an argument, even though both types are made up of three <code>i32</code>
values. Otherwise, tuple struct instances are similar to tuples in that you can
destructure them into their individual pieces, and you can use a <code>.</code> followed
by the index to access an individual value. Unlike tuples, tuple structs
require you to name the type of the struct when you destructure them. For
example, we would write <code>let Point(x, y, z) = origin;</code> to destructure the
values in the <code>origin</code> point into variables named <code>x</code>, <code>y</code>, and <code>z</code>.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="unit-like-structs-without-any-fields"></a></p>
<h3 id="defining-unit-like-structs"><a class="header" href="#defining-unit-like-structs">Defining Unit-Like Structs</a></h3>
<p>You can also define structs that dont have any fields! These are called
<em>unit-like structs</em> because they behave similarly to <code>()</code>, the unit type that
we mentioned in <a href="../ch03/ch03-02-data-types.html#the-tuple-type">“The Tuple Type”</a><!-- ignore --> section. Unit-like
structs can be useful when you need to implement a trait on some type but dont
have any data that you want to store in the type itself. Well discuss traits
in Chapter 10. Heres an example of declaring and instantiating a unit struct
named <code>AlwaysEqual</code>:</p>
<figure class="listing">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024">struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}</code></pre>
</figure>
<p>To define <code>AlwaysEqual</code>, we use the <code>struct</code> keyword, the name we want, and
then a semicolon. No need for curly brackets or parentheses! Then, we can get
an instance of <code>AlwaysEqual</code> in the <code>subject</code> variable in a similar way: using
the name we defined, without any curly brackets or parentheses. Imagine that
later well implement behavior for this type such that every instance of
<code>AlwaysEqual</code> is always equal to every instance of any other type, perhaps to
have a known result for testing purposes. We wouldnt need any data to
implement that behavior! Youll see in Chapter 10 how to define traits and
implement them on any type, including unit-like structs.</p>
<section class="note" aria-role="note">
<h3 id="ownership-of-struct-data"><a class="header" href="#ownership-of-struct-data">Ownership of Struct Data</a></h3>
<p>In the <code>User</code> struct definition in Listing 5-1, we used the owned <code>String</code>
type rather than the <code>&amp;str</code> 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.</p>
<p>Its also possible for structs to store references to data owned by something
else, but to do so requires the use of <em>lifetimes</em>, a Rust feature that well
discuss in Chapter 10. Lifetimes ensure that the data referenced by a struct
is valid for as long as the struct is. Lets say you try to store a reference
in a struct without specifying lifetimes, like the following in
<em>src/main.rs</em>; this wont work:</p>
<figure class="listing">
<span class="file-name">Filename: src/main.rs</span>
<!-- CAN'T EXTRACT SEE https://github.com/rust-lang/mdBook/issues/1127 -->
<pre><code class="language-rust ignore does_not_compile">struct User {
active: bool,
username: &amp;str,
email: &amp;str,
sign_in_count: u64,
}
fn main() {
let user1 = User {
active: true,
username: "someusername123",
email: "someone@example.com",
sign_in_count: 1,
};
}</code></pre>
</figure>
<p>The compiler will complain that it needs lifetime specifiers:</p>
<pre><code class="language-console">$ cargo run
Compiling structs v0.1.0 (file:///projects/structs)
error[E0106]: missing lifetime specifier
--&gt; src/main.rs:3:15
|
3 | username: &amp;str,
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
1 ~ struct User&lt;'a&gt; {
2 | active: bool,
3 ~ username: &amp;'a str,
|
error[E0106]: missing lifetime specifier
--&gt; src/main.rs:4:12
|
4 | email: &amp;str,
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
1 ~ struct User&lt;'a&gt; {
2 | active: bool,
3 | username: &amp;str,
4 ~ email: &amp;'a str,
|
For more information about this error, try `rustc --explain E0106`.
error: could not compile `structs` (bin "structs") due to 2 previous errors
</code></pre>
<p>In Chapter 10, well discuss how to fix these errors so that you can store
references in structs, but for now, well fix errors like these using owned
types like <code>String</code> instead of references like <code>&amp;str</code>.</p>
</section>
<!-- manual-regeneration
for the error above
after running update-rustc.sh:
pbcopy < listings/ch05-using-structs-to-structure-related-data/no-listing-02-reference-in-struct/output.txt
paste above
add `> ` before every line -->
</body>
</html>