2015-04-07 21:16:02 -05:00
|
|
|
|
% Structs
|
|
|
|
|
|
2015-09-02 20:58:18 -05:00
|
|
|
|
`struct`s are a way of creating more complex data types. For example, if we were
|
2015-04-10 09:37:29 -05:00
|
|
|
|
doing calculations involving coordinates in 2D space, we would need both an `x`
|
|
|
|
|
and a `y` value:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
let origin_x = 0;
|
|
|
|
|
let origin_y = 0;
|
|
|
|
|
```
|
|
|
|
|
|
2015-09-02 20:58:18 -05:00
|
|
|
|
A `struct` lets us combine these two into a single, unified datatype:
|
2015-04-07 21:16:02 -05:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct Point {
|
|
|
|
|
x: i32,
|
|
|
|
|
y: i32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let origin = Point { x: 0, y: 0 }; // origin: Point
|
|
|
|
|
|
|
|
|
|
println!("The origin is at ({}, {})", origin.x, origin.y);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2015-04-18 14:54:33 -05:00
|
|
|
|
There’s a lot going on here, so let’s break it down. We declare a `struct` with
|
|
|
|
|
the `struct` keyword, and then with a name. By convention, `struct`s begin with
|
|
|
|
|
a capital letter and are camel cased: `PointInSpace`, not `Point_In_Space`.
|
2015-04-07 21:16:02 -05:00
|
|
|
|
|
2015-09-02 20:58:18 -05:00
|
|
|
|
We can create an instance of our `struct` via `let`, as usual, but we use a `key:
|
2015-04-18 14:54:33 -05:00
|
|
|
|
value` style syntax to set each field. The order doesn’t need to be the same as
|
2015-04-07 21:16:02 -05:00
|
|
|
|
in the original declaration.
|
|
|
|
|
|
|
|
|
|
Finally, because fields have names, we can access the field through dot
|
|
|
|
|
notation: `origin.x`.
|
|
|
|
|
|
2015-09-02 20:58:18 -05:00
|
|
|
|
The values in `struct`s are immutable by default, like other bindings in Rust.
|
2015-04-07 21:16:02 -05:00
|
|
|
|
Use `mut` to make them mutable:
|
|
|
|
|
|
2015-04-10 09:37:29 -05:00
|
|
|
|
```rust
|
2015-04-07 21:16:02 -05:00
|
|
|
|
struct Point {
|
|
|
|
|
x: i32,
|
|
|
|
|
y: i32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let mut point = Point { x: 0, y: 0 };
|
|
|
|
|
|
|
|
|
|
point.x = 5;
|
|
|
|
|
|
|
|
|
|
println!("The point is at ({}, {})", point.x, point.y);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This will print `The point is at (5, 0)`.
|
2015-04-10 09:37:29 -05:00
|
|
|
|
|
2015-04-14 12:41:31 -05:00
|
|
|
|
Rust does not support field mutability at the language level, so you cannot
|
|
|
|
|
write something like this:
|
2015-04-10 09:37:29 -05:00
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
struct Point {
|
|
|
|
|
mut x: i32,
|
|
|
|
|
y: i32,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Mutability is a property of the binding, not of the structure itself. If you’re
|
|
|
|
|
used to field-level mutability, this may seem strange at first, but it
|
|
|
|
|
significantly simplifies things. It even lets you make things mutable for a short
|
|
|
|
|
time only:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
|
struct Point {
|
|
|
|
|
x: i32,
|
|
|
|
|
y: i32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let mut point = Point { x: 0, y: 0 };
|
|
|
|
|
|
|
|
|
|
point.x = 5;
|
|
|
|
|
|
2015-04-14 12:41:31 -05:00
|
|
|
|
let point = point; // this new binding can’t change now
|
2015-04-10 09:37:29 -05:00
|
|
|
|
|
2015-04-14 12:41:31 -05:00
|
|
|
|
point.y = 6; // this causes an error
|
2015-04-10 09:37:29 -05:00
|
|
|
|
}
|
|
|
|
|
```
|
2015-04-21 10:18:33 -05:00
|
|
|
|
|
|
|
|
|
# Update syntax
|
|
|
|
|
|
|
|
|
|
A `struct` can include `..` to indicate that you want to use a copy of some
|
2015-09-02 20:58:18 -05:00
|
|
|
|
other `struct` for some of the values. For example:
|
2015-04-21 10:18:33 -05:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct Point3d {
|
|
|
|
|
x: i32,
|
|
|
|
|
y: i32,
|
|
|
|
|
z: i32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut point = Point3d { x: 0, y: 0, z: 0 };
|
|
|
|
|
point = Point3d { y: 1, .. point };
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This gives `point` a new `y`, but keeps the old `x` and `z` values. It doesn’t
|
|
|
|
|
have to be the same `struct` either, you can use this syntax when making new
|
|
|
|
|
ones, and it will copy the values you don’t specify:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
# struct Point3d {
|
|
|
|
|
# x: i32,
|
|
|
|
|
# y: i32,
|
|
|
|
|
# z: i32,
|
|
|
|
|
# }
|
|
|
|
|
let origin = Point3d { x: 0, y: 0, z: 0 };
|
|
|
|
|
let point = Point3d { z: 1, x: 2, .. origin };
|
|
|
|
|
```
|
2015-05-12 13:13:03 -05:00
|
|
|
|
|
|
|
|
|
# Tuple structs
|
|
|
|
|
|
|
|
|
|
Rust has another data type that’s like a hybrid between a [tuple][tuple] and a
|
2015-09-02 20:58:18 -05:00
|
|
|
|
`struct`, called a ‘tuple struct’. Tuple structs have a name, but
|
2015-05-12 13:13:03 -05:00
|
|
|
|
their fields don’t:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct Color(i32, i32, i32);
|
|
|
|
|
struct Point(i32, i32, i32);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
[tuple]: primitive-types.html#tuples
|
|
|
|
|
|
|
|
|
|
These two will not be equal, even if they have the same values:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
# struct Color(i32, i32, i32);
|
|
|
|
|
# struct Point(i32, i32, i32);
|
|
|
|
|
let black = Color(0, 0, 0);
|
|
|
|
|
let origin = Point(0, 0, 0);
|
|
|
|
|
```
|
|
|
|
|
|
2015-09-02 20:58:18 -05:00
|
|
|
|
It is almost always better to use a `struct` than a tuple struct. We would write
|
2015-05-12 13:13:03 -05:00
|
|
|
|
`Color` and `Point` like this instead:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct Color {
|
|
|
|
|
red: i32,
|
|
|
|
|
blue: i32,
|
|
|
|
|
green: i32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Point {
|
|
|
|
|
x: i32,
|
|
|
|
|
y: i32,
|
|
|
|
|
z: i32,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Now, we have actual names, rather than positions. Good names are important,
|
2015-09-02 20:58:18 -05:00
|
|
|
|
and with a `struct`, we have actual names.
|
2015-05-12 13:13:03 -05:00
|
|
|
|
|
|
|
|
|
There _is_ one case when a tuple struct is very useful, though, and that’s a
|
|
|
|
|
tuple struct with only one element. We call this the ‘newtype’ pattern, because
|
|
|
|
|
it allows you to create a new type, distinct from that of its contained value
|
|
|
|
|
and expressing its own semantic meaning:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct Inches(i32);
|
|
|
|
|
|
|
|
|
|
let length = Inches(10);
|
|
|
|
|
|
|
|
|
|
let Inches(integer_length) = length;
|
|
|
|
|
println!("length is {} inches", integer_length);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
As you can see here, you can extract the inner integer type through a
|
|
|
|
|
destructuring `let`, just as with regular tuples. In this case, the
|
|
|
|
|
`let Inches(integer_length)` assigns `10` to `integer_length`.
|
2015-05-12 15:13:34 -05:00
|
|
|
|
|
|
|
|
|
# Unit-like structs
|
|
|
|
|
|
2015-09-02 20:58:18 -05:00
|
|
|
|
You can define a `struct` with no members at all:
|
2015-05-12 15:13:34 -05:00
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct Electron;
|
|
|
|
|
```
|
|
|
|
|
|
2015-09-02 20:58:18 -05:00
|
|
|
|
Such a `struct` is called ‘unit-like’ because it resembles the empty
|
2015-05-12 15:13:34 -05:00
|
|
|
|
tuple, `()`, sometimes called ‘unit’. Like a tuple struct, it defines a
|
|
|
|
|
new type.
|
|
|
|
|
|
|
|
|
|
This is rarely useful on its own (although sometimes it can serve as a
|
|
|
|
|
marker type), but in combination with other features, it can become
|
|
|
|
|
useful. For instance, a library may ask you to create a structure that
|
|
|
|
|
implements a certain [trait][trait] to handle events. If you don’t have
|
|
|
|
|
any data you need to store in the structure, you can just create a
|
2015-09-02 20:58:18 -05:00
|
|
|
|
unit-like `struct`.
|
2015-05-14 13:20:24 -05:00
|
|
|
|
|
|
|
|
|
[trait]: traits.html
|