about summary refs log tree commit diff
path: root/doc/tutorial/data.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/tutorial/data.md')
-rw-r--r--doc/tutorial/data.md291
1 files changed, 291 insertions, 0 deletions
diff --git a/doc/tutorial/data.md b/doc/tutorial/data.md
new file mode 100644
index 00000000000..25135787f4d
--- /dev/null
+++ b/doc/tutorial/data.md
@@ -0,0 +1,291 @@
+# Datatypes
+
+Rust datatypes are, by default, immutable. The core datatypes of Rust
+are structural records and 'tags' (tagged unions, algebraic data
+types).
+
+    type point = {x: float, y: float};
+    tag shape {
+        circle(point, float);
+        rectangle(point, point);
+    }
+    let my_shape = circle({x: 0.0, y: 0.0}, 10.0);
+
+## Records
+
+Rust record types are written `{field1: TYPE, field2: TYPE [,
+...]}`, and 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).
+
+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...
+
+    type stack = {content: [int], mutable head: uint};
+
+With such a type, you can do `mystack.head += 1u`. When the `mutable`
+is 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`:
+
+    let newpoint = {x: 0f with oldpoint};
+
+This will create a new struct, 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:
+float}` is not just a way to define a new type, but is the actual name
+of the type. Record types can be used without first defining them. If
+module A defines `type point = {x: float, y: float}`, and module B,
+without knowing anything about A, defines a function that returns an
+`{x: float, y: float}`, you can use that return value as a `point` in
+module A. (Remember that `type` defines an additional name for a type,
+not an actual new type.)
+
+## Record patterns
+
+Records can be destructured on in `alt` patterns. The basic syntax is
+`{fieldname: pattern, ...}`, but the pattern for a field can be
+omitted as a shorthand for simply binding the variable with the same
+name as the field.
+
+    alt mypoint {
+        {x: 0f, y: y_name} { /* Provide sub-patterns for fields */ }
+        {x, y}             { /* Simply bind the fields */ }
+    }
+
+When you are not interested in all the fields of a record, a record
+pattern may end with `, _` (as in `{field1, _}`) to indicate that
+you're ignoring all other fields.
+
+## Tags
+
+Tags [FIXME terminology] are datatypes that have several different
+representations. For example, the type shown earlier:
+
+    tag shape {
+        circle(point, float);
+        rectangle(point, point);
+    }
+
+A value of this type is either a circle¸ in which case it contains a
+point record and a float, or a rectangle, in which case it contains
+two point records. The run-time representation of such a value
+includes an identifier of the actual form that it holds, much like the
+'tagged union' pattern in C, but with better ergonomics.
+
+The above declaration will define a type `shape` that can be used to
+refer to such shapes, and two functions, `circle` and `rectangle`,
+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.
+
+Tag variants do not have to have parameters. This, for example, is
+equivalent to an `enum` in C:
+
+    tag direction {
+        north;
+        east;
+        south;
+        west;
+    };
+
+This will define `north`, `east`, `south`, and `west` as constants,
+all of which have type `direction`.
+
+There is a special case for tags with a single variant. These are used
+to define new types in such a way that the new name is not just a
+synonym for an existing type, but its own distinct type. If you say:
+
+    tag gizmo_id = int;
+
+That is a shorthand for this:
+
+    tag gizmo_id { gizmo_id(int); }
+
+Tag types like this can have their content extracted with the
+dereference (`*`) unary operator:
+
+    let my_gizmo_id = gizmo_id(10);
+    let id_int: int = *my_gizmo_id;
+
+## Tag patterns
+
+For tag types with multiple variants, destructuring is the only way to
+get at their contents. All variant constructors can be used as
+patterns, as in this definition of `area`:
+
+    fn area(sh: shape) -> float {
+        alt sh {
+            circle(_, size) { std::math::pi * size * size }
+            rectangle({x, y}, {x: x2, y: y2}) { (x2 - x) * (y2 - y) }
+        }
+    }
+
+For variants without arguments, you have to write `variantname.` (with
+a dot at the end) to match them in a pattern. This to prevent
+ambiguity between matching a variant name and binding a new variable.
+
+    fn point_from_direction(dir: direction) -> point {
+        alt dir {
+            north. { {x:  0f, y:  1f} }
+            east.  { {x:  1f, y:  0f} }
+            south. { {x:  0f, y: -1f} }
+            west.  { {x: -1f, y:  0f} }
+        }
+    }
+
+## Tuples
+
+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).
+
+    let mytup: (int, int, float) = (10, 20, 30.0);
+    alt mytup {
+      (a, b, c) { log a + b + (c as int); }
+    }
+
+## Pointers
+
+In contrast to a lot of modern languages, record and tag types in Rust
+are not represented as pointers to allocated memory. They are, like in
+C and C++, represented directly. This means that if you `let x = {x:
+1f, y: 1f};`, you are creating a record on the stack. If you then copy
+it into a data structure, the whole record is copied, not just a
+pointer.
+
+For small records like `point`, this is usually still more efficient
+than 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 `*TYPE`, 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 `@TYPE` for shared,
+reference-counted boxes, and `~TYPE`, for uniquely-owned pointers.
+
+All pointer types can be dereferenced with the `*` unary operator.
+
+### Shared boxes
+
+Shared boxes are pointers to heap-allocated, reference counted memory.
+A cycle collector ensures that circular references do not result in
+memory leaks.
+
+Creating a shared box is done by simply applying the binary `@`
+operator to an expression. The result of the expression will be boxed,
+resulting in a box of the right type. For example:
+
+    let x = @10; // New box, refcount of 1
+    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 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.
+
+    let x = ~10;
+    let y <- x;
+
+This is where the 'move' (`<-`) operator comes in. It is similar to
+`=`, but it de-initializes its source. Thus, the unique box can move
+from `x` to `y`, without violating the constraint that it only has a
+single owner.
+
+NOTE: If you do `y = x` instead, the box will be copied. We should
+emit warning for this, or disallow it entirely, but do not currently
+do so.
+
+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
+become the sole owner of the box.
+
+### Mutability
+
+All pointer types have a mutable variant, written `@mutable TYPE` or
+`~mutable TYPE`. Given such a pointer, you can write to its contents
+by combining the dereference operator with a mutating action.
+
+    fn increase_contents(pt: @mutable int) {
+        *pt += 1;
+    }
+
+## Vectors
+
+Rust vectors are always heap-allocated and unique. A value of type
+`[TYPE]` is represented by a pointer to a section of heap memory
+containing any number of `TYPE` values.
+
+NOTE: This uniqueness is turning out to be quite awkward in practice,
+and might change.
+
+Vector literals are enclosed in square brackets. Dereferencing is done
+with square brackets (and zero-based):
+
+    let myvec = [true, false, true, false];
+    if myvec[1] { std::io::println("boom"); }
+
+By default, vectors are immutable—you can not replace their elements.
+The type written as `[mutable TYPE]` is a vector with mutable
+elements. Mutable vector literals are written `[mutable]` (empty) or
+`[mutable 1, 2, 3]` (with elements).
+
+Growing a vector in Rust is not as inefficient as it looks (the `+`
+operator means concatenation when applied to vector types):
+
+    let myvec = [], i = 0;
+    while i < 100 {
+        myvec += [i];
+        i += 1;
+    }
+
+Because a vector is unique, replacing it with a longer one (which is
+what `+= [i]` does) is indistinguishable from appending to it
+in-place. Vector representations are optimized to grow
+logarithmically, so the above code generates about the same amount of
+copying and reallocation as `push` implementations in most other
+languages.
+
+## Strings
+
+The `str` type in Rust is represented exactly the same way as a vector
+of bytes (`[u8]`), except that it is guaranteed to have a trailing
+null byte (for interoperability with C APIs).
+
+This sequence of bytes is interpreted as an UTF-8 encoded sequence of
+characters. This has the advantage that UTF-8 encoded I/O (which
+should really be the goal for modern systems) is very fast, and that
+strings have, for most intents and purposes, a nicely compact
+representation. It has the disadvantage that you only get
+constant-time access by byte, not by character.
+
+A lot of algorithms don't need constant-time indexed access (they
+iterate over all characters, which `std::str::chars` helps with), and
+for those that do, many don't need actual characters, and can operate
+on bytes. For algorithms that do really need to index by character,
+there's the option to convert your string to a character vector (using
+`std::str::to_chars`).
+
+Like vectors, strings are always unique. You can wrap them in a shared
+box to share them. Unlike vectors, there is no mutable variant of
+strings. They are always immutable.
+
+## Resources
+
+FIXME fill this in