]>
Commit | Line | Data |
---|---|---|
8bb4bdeb | 1 | # Structs |
9346a6ac | 2 | |
e9174d1e | 3 | `struct`s are a way of creating more complex data types. For example, if we were |
9346a6ac AL |
4 | doing calculations involving coordinates in 2D space, we would need both an `x` |
5 | and a `y` value: | |
6 | ||
7 | ```rust | |
8 | let origin_x = 0; | |
9 | let origin_y = 0; | |
10 | ``` | |
11 | ||
9cc50fc6 SL |
12 | A `struct` lets us combine these two into a single, unified datatype with `x` |
13 | and `y` as field labels: | |
9346a6ac AL |
14 | |
15 | ```rust | |
16 | struct Point { | |
17 | x: i32, | |
18 | y: i32, | |
19 | } | |
20 | ||
21 | fn main() { | |
22 | let origin = Point { x: 0, y: 0 }; // origin: Point | |
23 | ||
24 | println!("The origin is at ({}, {})", origin.x, origin.y); | |
25 | } | |
26 | ``` | |
27 | ||
bd371182 AL |
28 | There’s a lot going on here, so let’s break it down. We declare a `struct` with |
29 | the `struct` keyword, and then with a name. By convention, `struct`s begin with | |
30 | a capital letter and are camel cased: `PointInSpace`, not `Point_In_Space`. | |
9346a6ac | 31 | |
e9174d1e | 32 | We can create an instance of our `struct` via `let`, as usual, but we use a `key: |
bd371182 | 33 | value` style syntax to set each field. The order doesn’t need to be the same as |
9346a6ac AL |
34 | in the original declaration. |
35 | ||
9cc50fc6 | 36 | Finally, because fields have names, we can access them through dot |
9346a6ac AL |
37 | notation: `origin.x`. |
38 | ||
e9174d1e | 39 | The values in `struct`s are immutable by default, like other bindings in Rust. |
9346a6ac AL |
40 | Use `mut` to make them mutable: |
41 | ||
42 | ```rust | |
43 | struct Point { | |
44 | x: i32, | |
45 | y: i32, | |
46 | } | |
47 | ||
48 | fn main() { | |
49 | let mut point = Point { x: 0, y: 0 }; | |
50 | ||
51 | point.x = 5; | |
52 | ||
53 | println!("The point is at ({}, {})", point.x, point.y); | |
54 | } | |
55 | ``` | |
56 | ||
57 | This will print `The point is at (5, 0)`. | |
58 | ||
59 | Rust does not support field mutability at the language level, so you cannot | |
60 | write something like this: | |
61 | ||
62 | ```rust,ignore | |
63 | struct Point { | |
476ff2be | 64 | mut x: i32, // This causes an error. |
9346a6ac AL |
65 | y: i32, |
66 | } | |
67 | ``` | |
68 | ||
69 | Mutability is a property of the binding, not of the structure itself. If you’re | |
70 | used to field-level mutability, this may seem strange at first, but it | |
9cc50fc6 SL |
71 | significantly simplifies things. It even lets you make things mutable on a temporary |
72 | basis: | |
9346a6ac AL |
73 | |
74 | ```rust,ignore | |
75 | struct Point { | |
76 | x: i32, | |
77 | y: i32, | |
78 | } | |
79 | ||
80 | fn main() { | |
81 | let mut point = Point { x: 0, y: 0 }; | |
82 | ||
83 | point.x = 5; | |
84 | ||
476ff2be | 85 | let point = point; // `point` is now immutable. |
9346a6ac | 86 | |
476ff2be | 87 | point.y = 6; // This causes an error. |
9346a6ac AL |
88 | } |
89 | ``` | |
bd371182 | 90 | |
8bb4bdeb | 91 | Your structure can still contain `&mut` references, which will let |
9cc50fc6 SL |
92 | you do some kinds of mutation: |
93 | ||
94 | ```rust | |
95 | struct Point { | |
96 | x: i32, | |
97 | y: i32, | |
98 | } | |
99 | ||
100 | struct PointRef<'a> { | |
101 | x: &'a mut i32, | |
102 | y: &'a mut i32, | |
103 | } | |
104 | ||
105 | fn main() { | |
106 | let mut point = Point { x: 0, y: 0 }; | |
107 | ||
108 | { | |
109 | let r = PointRef { x: &mut point.x, y: &mut point.y }; | |
110 | ||
111 | *r.x = 5; | |
112 | *r.y = 6; | |
113 | } | |
114 | ||
115 | assert_eq!(5, point.x); | |
116 | assert_eq!(6, point.y); | |
117 | } | |
118 | ``` | |
119 | ||
8bb4bdeb XL |
120 | Initialization of a data structure (struct, enum, union) can be simplified when |
121 | fields of the data structure are initialized with variables of the same | |
122 | names as the fields. | |
123 | ||
7cac9316 | 124 | ```rust |
8bb4bdeb XL |
125 | #[derive(Debug)] |
126 | struct Person<'a> { | |
127 | name: &'a str, | |
128 | age: u8 | |
129 | } | |
130 | ||
131 | fn main() { | |
132 | // Create struct with field init shorthand | |
133 | let name = "Peter"; | |
134 | let age = 27; | |
135 | let peter = Person { name, age }; | |
136 | ||
137 | // Debug-print struct | |
138 | println!("{:?}", peter); | |
139 | } | |
140 | ``` | |
141 | ||
bd371182 AL |
142 | # Update syntax |
143 | ||
144 | A `struct` can include `..` to indicate that you want to use a copy of some | |
e9174d1e | 145 | other `struct` for some of the values. For example: |
bd371182 AL |
146 | |
147 | ```rust | |
148 | struct Point3d { | |
149 | x: i32, | |
150 | y: i32, | |
151 | z: i32, | |
152 | } | |
153 | ||
154 | let mut point = Point3d { x: 0, y: 0, z: 0 }; | |
155 | point = Point3d { y: 1, .. point }; | |
156 | ``` | |
157 | ||
158 | This gives `point` a new `y`, but keeps the old `x` and `z` values. It doesn’t | |
159 | have to be the same `struct` either, you can use this syntax when making new | |
160 | ones, and it will copy the values you don’t specify: | |
161 | ||
162 | ```rust | |
163 | # struct Point3d { | |
164 | # x: i32, | |
165 | # y: i32, | |
166 | # z: i32, | |
167 | # } | |
168 | let origin = Point3d { x: 0, y: 0, z: 0 }; | |
169 | let point = Point3d { z: 1, x: 2, .. origin }; | |
170 | ``` | |
171 | ||
172 | # Tuple structs | |
173 | ||
174 | Rust has another data type that’s like a hybrid between a [tuple][tuple] and a | |
9cc50fc6 SL |
175 | `struct`, called a ‘tuple struct’. Tuple structs have a name, but their fields |
176 | don't. They are declared with the `struct` keyword, and then with a name | |
177 | followed by a tuple: | |
178 | ||
179 | [tuple]: primitive-types.html#tuples | |
bd371182 AL |
180 | |
181 | ```rust | |
182 | struct Color(i32, i32, i32); | |
183 | struct Point(i32, i32, i32); | |
bd371182 | 184 | |
bd371182 AL |
185 | let black = Color(0, 0, 0); |
186 | let origin = Point(0, 0, 0); | |
187 | ``` | |
188 | ||
5bcae85e SL |
189 | Here, `black` and `origin` are not the same type, even though they contain the |
190 | same values. | |
191 | ||
192 | The members of a tuple struct may be accessed by dot notation or destructuring | |
193 | `let`, just like regular tuples: | |
194 | ||
195 | ```rust | |
196 | # struct Color(i32, i32, i32); | |
197 | # struct Point(i32, i32, i32); | |
198 | # let black = Color(0, 0, 0); | |
199 | # let origin = Point(0, 0, 0); | |
200 | let black_r = black.0; | |
201 | let Point(_, origin_y, origin_z) = origin; | |
202 | ``` | |
203 | ||
204 | Patterns like `Point(_, origin_y, origin_z)` are also used in | |
205 | [match expressions][match]. | |
206 | ||
207 | One case when a tuple struct is very useful is when it has only one element. | |
208 | We call this the ‘newtype’ pattern, because it allows you to create a new type | |
209 | that is distinct from its contained value and also expresses its own semantic | |
210 | meaning: | |
211 | ||
212 | ```rust | |
213 | struct Inches(i32); | |
214 | ||
215 | let length = Inches(10); | |
216 | ||
217 | let Inches(integer_length) = length; | |
218 | println!("length is {} inches", integer_length); | |
219 | ``` | |
220 | ||
221 | As above, you can extract the inner integer type through a destructuring `let`. | |
222 | In this case, the `let Inches(integer_length)` assigns `10` to `integer_length`. | |
223 | We could have used dot notation to do the same thing: | |
224 | ||
225 | ```rust | |
226 | # struct Inches(i32); | |
227 | # let length = Inches(10); | |
228 | let integer_length = length.0; | |
229 | ``` | |
230 | ||
231 | It's always possible to use a `struct` instead of a tuple struct, and can be | |
232 | clearer. We could write `Color` and `Point` like this instead: | |
bd371182 AL |
233 | |
234 | ```rust | |
235 | struct Color { | |
236 | red: i32, | |
237 | blue: i32, | |
238 | green: i32, | |
239 | } | |
240 | ||
241 | struct Point { | |
242 | x: i32, | |
243 | y: i32, | |
244 | z: i32, | |
245 | } | |
246 | ``` | |
247 | ||
9cc50fc6 SL |
248 | Good names are important, and while values in a tuple struct can be |
249 | referenced with dot notation as well, a `struct` gives us actual names, | |
250 | rather than positions. | |
bd371182 | 251 | |
5bcae85e | 252 | [match]: match.html |
bd371182 AL |
253 | |
254 | # Unit-like structs | |
255 | ||
e9174d1e | 256 | You can define a `struct` with no members at all: |
bd371182 | 257 | |
8bb4bdeb | 258 | ```rust,compile_fail,E0423 |
476ff2be SL |
259 | struct Electron {} // Use empty braces... |
260 | struct Proton; // ...or just a semicolon. | |
b039eaaf | 261 | |
8bb4bdeb | 262 | // Use the same notation when creating an instance. |
5bcae85e SL |
263 | let x = Electron {}; |
264 | let y = Proton; | |
8bb4bdeb | 265 | let z = Electron; // Error |
bd371182 AL |
266 | ``` |
267 | ||
e9174d1e | 268 | Such a `struct` is called ‘unit-like’ because it resembles the empty |
bd371182 AL |
269 | tuple, `()`, sometimes called ‘unit’. Like a tuple struct, it defines a |
270 | new type. | |
271 | ||
272 | This is rarely useful on its own (although sometimes it can serve as a | |
273 | marker type), but in combination with other features, it can become | |
274 | useful. For instance, a library may ask you to create a structure that | |
275 | implements a certain [trait][trait] to handle events. If you don’t have | |
9cc50fc6 | 276 | any data you need to store in the structure, you can create a |
e9174d1e | 277 | unit-like `struct`. |
d9579d0f AL |
278 | |
279 | [trait]: traits.html |