tutorial: Expand the section on datatypes

This commit is contained in:
Brian Anderson 2012-07-06 18:09:59 -07:00
parent ad05996223
commit af199f2edb

View File

@ -1252,17 +1252,32 @@ fn contains(v: ~[int], elt: int) -> bool {
# Datatypes
Rust datatypes are, by default, immutable. The core datatypes of Rust
are structural records and 'enums' (tagged unions, algebraic data
types).
The core datatypes of Rust are structural records, enums (tagged
unions, algebraic data types), and classes. They are immutable
by default.
~~~~
type point = {x: float, y: float};
enum shape {
circle(point, float),
rectangle(point, point)
}
let my_shape = circle({x: 0.0, y: 0.0}, 10.0);
class drawing {
let mut shapes: [shape];
new() {
self.shapes = [];
}
fn add_shape(new_shape: shape) {
self.shapes += [new_shape];
}
}
let my_drawing = drawing();
my_drawing.add_shape(circle({x: 0.0, y: 0.0}, 10.0));
~~~~
## Records
@ -1271,12 +1286,10 @@ Rust record types are written `{field1: T1, field2: T2 [, ...]}`,
where `T1`, `T2`, ... denote types. Record literals are written in
the same way, but with expressions instead of types. They are quite
similar to C structs, and even laid out the same way in memory (so you
can read from a Rust struct in C, and vice-versa).
can read from a Rust struct in C, and vice-versa). The dot operator is
used to access record fields (`mypoint.x`).
The dot operator is used to access record fields (`mypoint.x`).
Fields that you want to mutate must be explicitly marked as such. For
example...
Fields that you want to mutate must be explicitly marked `mut`.
~~~~
type stack = {content: ~[int], mut head: uint};
@ -1286,8 +1299,8 @@ With such a type, you can do `mystack.head += 1u`. If `mut` were
omitted from the type, such an assignment would result in a type
error.
To 'update' an immutable record, you use functional record update
syntax, by ending a record literal with the keyword `with`:
To create a new record based on the value of an existing record
you construct it using the `with` keyword:
~~~~
let oldpoint = {x: 10f, y: 20f};
@ -1295,7 +1308,7 @@ let newpoint = {x: 0f with oldpoint};
assert newpoint == {x: 0f, y: 20f};
~~~~
This will create a new struct, copying all the fields from `oldpoint`
This will create a new record, copying all the fields from `oldpoint`
into it, except for the ones that are explicitly set in the literal.
Rust record types are *structural*. This means that `{x: float, y:
@ -1329,8 +1342,8 @@ the fields of a record, a record pattern may end with `, _` (as in
## Enums
Enums are datatypes that have several different representations. For
example, the type shown earlier:
Enums are datatypes that have several alternate representations. For
example, consider the type shown earlier:
~~~~
# type point = {x: float, y: float};
@ -1352,7 +1365,7 @@ which can be used to construct values of the type (taking arguments of
the specified types). So `circle({x: 0f, y: 0f}, 10f)` is the way to
create a new circle.
Enum variants do not have to have parameters. This, for example, is
Enum variants need not have type parameters. This, for example, is
equivalent to a C enum:
~~~~
@ -1448,8 +1461,8 @@ fn point_from_direction(dir: direction) -> point {
Tuples in Rust behave exactly like records, except that their fields
do not have names (and can thus not be accessed with dot notation).
Tuples can have any arity except for 0 or 1 (though you may see nil,
`()`, as the empty tuple if you like).
Tuples can have any arity except for 0 or 1 (though you may consider
nil, `()`, as the empty tuple if you like).
~~~~
let mytup: (int, int, float) = (10, 20, 30.0);
@ -1472,11 +1485,15 @@ allocating memory and going through a pointer. But for big records, or
records with mutable fields, it can be useful to have a single copy on
the heap, and refer to that through a pointer.
Rust supports several types of pointers. The simplest is the unsafe
pointer, written `*T`, which is a completely unchecked pointer type
only used in unsafe code (and thus, in typical Rust code, very
rarely). The safe pointer types are `@T` for shared, reference-counted
boxes, and `~T`, for uniquely-owned pointers.
Rust supports several types of pointers. The safe pointer types are
`@T` for shared boxes allocated on the local heap, `~T`, for
uniquely-owned boxes allocated on the exchange heap, and `&T`, for
borrowed pointers, which may point to any memory, and whose lifetimes
are governed by the call stack.
Rust also has an unsafe pointer, written `*T`, which is a completely
unchecked pointer type only used in unsafe code (and thus, in typical
Rust code, very rarely).
All pointer types can be dereferenced with the `*` unary operator.
@ -1496,20 +1513,32 @@ let y = x; // Copy the pointer, increase refcount
// When x and y go out of scope, refcount goes to 0, box is freed
~~~~
***Note:*** We may in the future switch to garbage collection, rather
***Note:*** We will in the future switch to garbage collection, rather
than reference counting, for shared boxes.
Shared boxes never cross task boundaries.
### Unique boxes
In contrast to shared boxes, unique boxes are not reference counted.
Instead, it is statically guaranteed that only a single owner of the
box exists at any time.
In contrast to shared boxes, unique boxes have a single owner and thus
two unique boxes may not refer to the same memory. All unique boxes
across all tasks are allocated on a single _exchange heap_, where
their uniquely owned nature allows them to be passed between tasks.
Because unique boxes are uniquely owned, copying them involves allocating
a new unique box and duplicating the contents. Copying unique boxes
is expensive so the compiler will complain if you do.
~~~~
let x = ~10;
let y <- x;
let y = x; // error: copying a non-implicitly copyable type
~~~~
If you really want to copy a unique box you must say so explicitly.
~~~~
let x = ~10;
let y = copy x;
~~~~
This is where the 'move' (`<-`) operator comes in. It is similar to
@ -1518,6 +1547,11 @@ from `x` to `y`, without violating the constraint that it only has a
single owner (if you used assignment instead of the move operator, the
box would, in principle, be copied).
~~~~
let x = ~10;
let y <- x;
~~~~
Unique boxes, when they do not contain any shared boxes, can be sent
to other tasks. The sending task will give up ownership of the box,
and won't be able to access it afterwards. The receiving task will