@@ -948,11 +948,281 @@ comments
948
948
949
949
## Compound Data Types
950
950
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.
952
954
953
- Structs
955
+ ### Tuples
954
956
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 ` .
956
1226
957
1227
## Match
958
1228
0 commit comments