Skip to content

Commit 11c64f1

Browse files
committed
Guide: complex data types
1 parent c9e2ca0 commit 11c64f1

File tree

1 file changed

+273
-3
lines changed

1 file changed

+273
-3
lines changed

src/doc/guide.md

Lines changed: 273 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -948,11 +948,281 @@ comments
948948

949949
## Compound Data Types
950950

951-
Tuples
951+
Rust, like many programming languages, has a number of different data types
952+
that are built-in. You've already done some simple work with integers and
953+
strings, but next, let's talk about some more complicated ways of storing data.
952954

953-
Structs
955+
### Tuples
954956

955-
Enums
957+
The first compound data type we're going to talk about are called **tuple**s.
958+
Tuples are an ordered list of a fixed size. Like this:
959+
960+
```rust
961+
let x = (1i, "hello");
962+
```
963+
964+
The parenthesis and commas form this two-length tuple. Here's the same code, but
965+
with the type annotated:
966+
967+
```rust
968+
let x: (int, &str) = (1, "hello");
969+
```
970+
971+
As you can see, the type of a tuple looks just like the tuple, but with each
972+
position having a type name rather than the value. Careful readers will also
973+
note that tuples are heterogeneous: we have an `int` and a `&str` in this tuple.
974+
You haven't seen `&str` as a type before, and we'll discuss the details of
975+
strings later. In systems programming languages, strings are a bit more complex
976+
than in other languages. For now, just read `&str` as "a string slice," and
977+
we'll learn more soon.
978+
979+
You can access the fields in a tuple through a **destructuring let**. Here's
980+
an example:
981+
982+
```rust
983+
let (x, y, z) = (1i, 2i, 3i);
984+
985+
println!("x is {}", x);
986+
```
987+
988+
Remember before when I said the left hand side of a `let` statement was more
989+
powerful than just assigning a binding? Here we are. We can put a pattern on
990+
the left hand side of the `let`, and if it matches up to the right hand side,
991+
we can assign multiple bindings at once. In this case, `let` 'destructures,'
992+
or 'breaks up,' the tuple, and assigns the bits to three bindings.
993+
994+
This pattern is very powerful, and we'll see it repeated more later.
995+
996+
The last thing to say about tuples is that they are only equivalent if
997+
the arity, types, and values are all identical.
998+
999+
```rust
1000+
let x = (1i, 2i, 3i);
1001+
let y = (2i, 3i, 4i);
1002+
1003+
if x == y {
1004+
println!("yes");
1005+
} else {
1006+
println!("no");
1007+
}
1008+
```
1009+
1010+
This will print `no`, as the values aren't equal.
1011+
1012+
One other use of tuples is to return multiple values from a function:
1013+
1014+
```rust
1015+
fn next_two(x: int) -> (int, int) { (x + 1i, x + 2i) }
1016+
1017+
fn main() {
1018+
let (x, y) = next_two(5i);
1019+
println!("x, y = {}, {}", x, y);
1020+
}
1021+
```
1022+
1023+
Even though Rust functions can only return one value, a tuple _is_ one value,
1024+
that happens to be made up of two. You can also see in this example how you
1025+
can destructure a pattern returned by a function, as well.
1026+
1027+
Tuples are a very simple data structure, and so are not often what you want.
1028+
Let's move on to their bigger sibling, structs.
1029+
1030+
### Structs
1031+
1032+
A struct is another form of a 'record type,' just like a tuple. There's a
1033+
difference: structs give each element that they contain a name, called a
1034+
'field' or a 'member.' Check it out:
1035+
1036+
```rust
1037+
struct Point {
1038+
x: int,
1039+
y: int,
1040+
}
1041+
1042+
fn main() {
1043+
let origin = Point { x: 0i, y: 0i };
1044+
1045+
println!("The origin is at ({}, {})", origin.x, origin.y);
1046+
}
1047+
```
1048+
1049+
There's a lot going on here, so let's break it down. We declare a struct with
1050+
the `struct` keyword, and then with a name. By convention, structs begin with a
1051+
capital letter and are also camel cased: `PointInSpace`, not `Point_In_Space`.
1052+
1053+
We can create an instance of our struct via `let`, as usual, but we use a `key:
1054+
value` style syntax to set each field. The order doesn't need to be the same as
1055+
in the original declaration.
1056+
1057+
Finally, because fields have names, we can access the field through dot
1058+
notation: `origin.x`.
1059+
1060+
The values in structs are immutable, like other bindings in Rust. However, you
1061+
can use `mut` to make them mutable:
1062+
1063+
```rust
1064+
struct Point {
1065+
x: int,
1066+
y: int,
1067+
}
1068+
1069+
fn main() {
1070+
let mut point = Point { x: 0i, y: 0i };
1071+
1072+
point.x = 5;
1073+
1074+
println!("The point is at ({}, {})", point.x, point.y);
1075+
}
1076+
```
1077+
1078+
This will print `The point is at (5, 0)`.
1079+
1080+
### Tuple Structs and Newtypes
1081+
1082+
Rust has another data type that's like a hybrid between a tuple and a struct,
1083+
called a **tuple struct**. Tuple structs do have a name, but their fields
1084+
don't:
1085+
1086+
1087+
```
1088+
struct Color(int, int, int);
1089+
struct Point(int, int, int);
1090+
```
1091+
1092+
These two will not be equal, even if they have the same values:
1093+
1094+
```{rust,ignore}
1095+
let black = Color(0, 0, 0);
1096+
let origin = Point(0, 0, 0);
1097+
```
1098+
1099+
It is almost always better to use a struct than a tuple struct. We would write
1100+
`Color` and `Point` like this instead:
1101+
1102+
```rust
1103+
struct Color {
1104+
red: int,
1105+
blue: int,
1106+
green: int,
1107+
}
1108+
1109+
struct Point {
1110+
x: int,
1111+
y: int,
1112+
z: int,
1113+
}
1114+
```
1115+
1116+
Now, we have actual names, rather than positions. Good names are important,
1117+
and with a struct, we have actual names.
1118+
1119+
There _is_ one case when a tuple struct is very useful, though, and that's a
1120+
tuple struct with only one element. We call this a 'newtype,' because it lets
1121+
you create a new type that's a synonym for another one:
1122+
1123+
```
1124+
struct Inches(int);
1125+
struct Centimeters(int);
1126+
1127+
let length = Inches(10);
1128+
1129+
let Inches(integer_length) = length;
1130+
println!("length is {} inches", integer_length);
1131+
```
1132+
1133+
As you can see here, you can extract the inner integer type through a
1134+
destructuring `let`.
1135+
1136+
### Enums
1137+
1138+
Finally, Rust has a "sum type", an **enum**. Enums are an incredibly useful
1139+
feature of Rust, and are used throughout the standard library. Enums look
1140+
like this:
1141+
1142+
```
1143+
enum Ordering {
1144+
Less,
1145+
Equal,
1146+
Greater,
1147+
}
1148+
```
1149+
1150+
This is an enum that is provided by the Rust standard library. An `Ordering`
1151+
can only be _one_ of `Less`, `Equal`, or `Greater` at any given time. Here's
1152+
an example:
1153+
1154+
```rust
1155+
let x = 5i;
1156+
let y = 10i;
1157+
1158+
let ordering = x.cmp(&y);
1159+
1160+
if ordering == Less {
1161+
println!("less");
1162+
} else if ordering == Greater {
1163+
println!("greater");
1164+
} else if ordering == Equal {
1165+
println!("equal");
1166+
}
1167+
```
1168+
1169+
`cmp` is a function that compares two things, and returns an `Ordering`. The
1170+
call looks a little bit strange: rather than `cmp(x, y)`, we say `x.cmp(&y)`.
1171+
We haven't covered methods and references yet, so it should look a little bit
1172+
foreign. Right now, just pretend it says `cmp(x, y)`, and we'll get to those
1173+
details soon.
1174+
1175+
The `ordering` variable has the type `Ordering`, and so contains one of the
1176+
three values. We can then do a bunch of `if`/`else` comparisons to check
1177+
which one it is.
1178+
1179+
However, repeated `if`/`else` comparisons get quite tedious. Rust has a feature
1180+
that not only makes them nicer to read, but also makes sure that you never
1181+
miss a case. Before we get to that, though, let's talk about another kind of
1182+
enum: one with values.
1183+
1184+
This enum has two variants, one of which has a value.:
1185+
1186+
```
1187+
enum OptionalInt {
1188+
Value(int),
1189+
Missing
1190+
}
1191+
1192+
fn main() {
1193+
let x = Value(5);
1194+
let y = Missing;
1195+
1196+
match x {
1197+
Value(n) => println!("x is {:d}", n),
1198+
Missing => println!("x is missing!"),
1199+
}
1200+
1201+
match y {
1202+
Value(n) => println!("y is {:d}", n),
1203+
Missing => println!("y is missing!"),
1204+
}
1205+
}
1206+
```
1207+
1208+
This enum represents an `int` that we may or may not have. In the `Missing`
1209+
case, we have no value, but in the `Value` case, we do. This enum is specific
1210+
to `int`s, though. We can make it usable by any type, but we haven't quite
1211+
gotten there yet!
1212+
1213+
You can have any number of values in an enum:
1214+
1215+
```
1216+
enum OptionalColor {
1217+
Color(int, int, int),
1218+
Missing
1219+
}
1220+
```
1221+
1222+
Enums with values are quite useful, but as I mentioned, they're even more
1223+
useful when they're generic across types. But before we get to generics, let's
1224+
talk about how to fix this big `if`/`else` statements we've been writing. We'll
1225+
do that with `match`.
9561226

9571227
## Match
9581228

0 commit comments

Comments
 (0)