Tutorial revisions (among other things, closes #2990).

This commit is contained in:
Lindsey Kuper 2012-07-22 19:12:51 -07:00
parent 9849c78c8e
commit d9cbdf7865
1 changed files with 72 additions and 70 deletions

View File

@ -15,28 +15,31 @@ the whole language, though not with the depth and precision of the
Rust is a systems programming language with a focus on type safety, Rust is a systems programming language with a focus on type safety,
memory safety, concurrency and performance. It is intended for writing memory safety, concurrency and performance. It is intended for writing
large, high performance applications while preventing several classes large, high-performance applications while preventing several classes
of errors commonly found in languages like C++. Rust has a of errors commonly found in languages like C++. Rust has a
sophisticated memory model that enables many of the efficient data sophisticated memory model that makes possible many of the efficient
structures used in C++ while disallowing invalid memory access that data structures used in C++, while disallowing invalid memory accesses
would otherwise cause segmentation faults. Like other systems that would otherwise cause segmentation faults. Like other systems
languages it is statically typed and compiled ahead of time. languages, it is statically typed and compiled ahead of time.
As a multi-paradigm language it has strong support for writing code in As a multi-paradigm language, Rust supports writing code in
procedural, functional and object-oriented styles. Some of it's nice procedural, functional and object-oriented styles. Some of its nice
high-level features include: high-level features include:
* Pattern matching and algebraic data types (enums) - common in functional * ***Pattern matching and algebraic data types (enums).*** Common in
languages, pattern matching on ADTs provides a compact and expressive functional languages, pattern matching on ADTs provides a compact
way to encode program logic and expressive way to encode program logic.
* Task-based concurrency - Rust uses lightweight tasks that do not share * ***Task-based concurrency.*** Rust uses lightweight tasks that do
memory not share memory.
* Higher-order functions - Closures in Rust are very powerful and used * ***Higher-order functions.*** Rust functions may take closures as
pervasively arguments or return closures as return values. Closures in Rust are
* Polymorphism - Rust's type system features a unique combination of very powerful and used pervasively.
Java-style interfaces and Haskell-style typeclasses * ***Interface polymorphism.*** Rust's type system features a unique
* Generics - Functions and types can be parameterized over generic combination of Java-style interfaces and Haskell-style typeclasses.
types with optional type constraints * ***Parametric polymorphism (generics).*** Functions and types can be
parameterized over type variables with optional type constraints.
* ***Type inference.*** Type annotations on local variable
declarations can be omitted.
## First impressions ## First impressions
@ -229,7 +232,7 @@ into an error.
## Anatomy of a Rust program ## Anatomy of a Rust program
In its simplest form, a Rust program is simply a `.rs` file with some In its simplest form, a Rust program is a `.rs` file with some
types and functions defined in it. If it has a `main` function, it can types and functions defined in it. If it has a `main` function, it can
be compiled to an executable. Rust does not allow code that's not a be compiled to an executable. Rust does not allow code that's not a
declaration to appear at the top level of the file—all statements must declaration to appear at the top level of the file—all statements must
@ -1181,61 +1184,60 @@ several of Rust's unique features as we encounter them.
Rust has three competing goals that inform its view of memory: Rust has three competing goals that inform its view of memory:
* Memory safety - memory that is managed by and is accessible to * Memory safety: memory that is managed by and is accessible to the
the Rust language must be guaranteed to be valid. Under normal Rust language must be guaranteed to be valid; under normal
circumstances it is impossible for Rust to trigger a segmentation circumstances it must be impossible for Rust to trigger a
fault or leak memory segmentation fault or leak memory
* Performance - high-performance low-level code tends to employ * Performance: high-performance low-level code must be able to employ
a number of allocation strategies. low-performance high-level a number of allocation strategies; low-performance high-level code
code often uses a single, GC-based, heap allocation strategy must be able to employ a single, garbage-collection-based, heap
* Concurrency - Rust must maintain memory safety guarantees even allocation strategy
for code running in parallel * Concurrency: Rust must maintain memory safety guarantees, even for
code running in parallel
## How performance considerations influence the memory model ## How performance considerations influence the memory model
Many languages that ofter the kinds of memory safety guarentees that Many languages that offer the kinds of memory safety guarantees that
Rust does have a single allocation strategy: objects live on the heap, Rust does have a single allocation strategy: objects live on the heap,
live for as long as they are needed, and are periodically garbage live for as long as they are needed, and are periodically
collected. This is very straightforword both conceptually and in garbage-collected. This approach is straightforward both in concept
implementation, but has very significant costs. Such languages tend to and in implementation, but has significant costs. Languages that take
aggressively pursue ways to ameliorate allocation costs (think the this approach tend to aggressively pursue ways to ameliorate
Java virtual machine). Rust supports this strategy with _shared allocation costs (think the Java Virtual Machine). Rust supports this
boxes_, memory allocated on the heap that may be referred to (shared) strategy with _shared boxes_: memory allocated on the heap that may be
by multiple variables. referred to (shared) by multiple variables.
In comparison, languages like C++ offer a very precise control over By comparison, languages like C++ offer very precise control over
where objects are allocated. In particular, it is common to put where objects are allocated. In particular, it is common to put them
them directly on the stack, avoiding expensive heap allocation. In directly on the stack, avoiding expensive heap allocation. In Rust
Rust this is possible as well, and the compiler will use a clever this is possible as well, and the compiler will use a clever _pointer
lifetime analysis to ensure that no variable can refer to stack lifetime analysis_ to ensure that no variable can refer to stack
objects after they are destroyed. objects after they are destroyed.
## How concurrency considerations influence the memory model ## How concurrency considerations influence the memory model
Memory safety in a concurrent environment tends to mean avoiding race Memory safety in a concurrent environment involves avoiding race
conditions between two threads of execution accessing the same conditions between two threads of execution accessing the same
memory. Even high-level languages frequently avoid solving this memory. Even high-level languages often require programmers to
problem, requiring programmers to correctly employ locking to unsure correctly employ locking to ensure that a program is free of races.
their program is free of races.
Rust starts from the position that memory simply cannot be shared Rust starts from the position that memory cannot be shared between
between tasks. Experience in other languages has proven that isolating tasks. Experience in other languages has proven that isolating each
each tasks' heap from each other is a reliable strategy and one that task's heap from the others is a reliable strategy and one that is
is easy for programmers to reason about. Having isolated heaps easy for programmers to reason about. Heap isolation has the
additionally means that garbage collection must only be done additional benefit that garbage collection must only be done
per-heap. Rust never 'stops the world' to garbage collect memory. per-heap. Rust never "stops the world" to garbage-collect memory.
If Rust tasks have completely isolated heaps then that seems to imply Complete isolation of heaps between tasks implies that any data
that any data transferred between them must be copied. While this transferred between tasks must be copied. While this is a fine and
is a fine and useful way to implement communication between tasks, useful way to implement communication between tasks, it is also very
it is also very inefficient for large data structures. inefficient for large data structures. Because of this, Rust also
employs a global _exchange heap_. Objects allocated in the exchange
Because of this Rust also introduces a global "exchange heap". Objects heap have _ownership semantics_, meaning that there is only a single
allocated here have _ownership semantics_, meaning that there is only variable that refers to them. For this reason, they are referred to as
a single variable that refers to them. For this reason they are _unique boxes_. All tasks may allocate objects on the exchange heap,
refered to as _unique boxes_. All tasks may allocate objects on this then transfer ownership of those objects to other tasks, avoiding
heap, then transfer ownership of those allocations to other tasks, expensive copies.
avoiding expensive copies.
## What to be aware of ## What to be aware of
@ -1249,11 +1251,11 @@ of each is key to using Rust effectively.
# Boxes and pointers # Boxes and pointers
In contrast to a lot of modern languages, aggregate types like records In contrast to a lot of modern languages, aggregate types like records
and enums are not represented as pointers to allocated memory. They and enums are _not_ represented as pointers to allocated memory in
are, like in C and C++, represented directly. This means that if you Rust. They are, as in C and C++, represented directly. This means that
`let x = {x: 1f, y: 1f};`, you are creating a record on the stack. If if you `let x = {x: 1f, y: 1f};`, you are creating a record on the
you then copy it into a data structure, the whole record is copied, stack. If you then copy it into a data structure, the whole record is
not just a pointer. copied, not just a pointer.
For small records like `point`, this is usually more efficient than For small records like `point`, this is usually more efficient than
allocating memory and going through a pointer. But for big records, or allocating memory and going through a pointer. But for big records, or
@ -1859,7 +1861,7 @@ like methods named 'new' and 'drop', but without 'fn', and without arguments
for drop. for drop.
In the constructor, the compiler will enforce that all fields are initialized In the constructor, the compiler will enforce that all fields are initialized
before doing anything which might allow them to be accessed. This includes before doing anything that might allow them to be accessed. This includes
returning from the constructor, calling any method on 'self', calling any returning from the constructor, calling any method on 'self', calling any
function with 'self' as an argument, or taking a reference to 'self'. Mutation function with 'self' as an argument, or taking a reference to 'self'. Mutation
of immutable fields is possible only in the constructor, and only before doing of immutable fields is possible only in the constructor, and only before doing
@ -2959,9 +2961,9 @@ other. The function `task::spawn_listener()` supports this pattern. We'll look
briefly at how it is used. briefly at how it is used.
To see how `spawn_listener()` works, we will create a child task To see how `spawn_listener()` works, we will create a child task
which receives `uint` messages, converts them to a string, and sends that receives `uint` messages, converts them to a string, and sends
the string in response. The child terminates when `0` is received. the string in response. The child terminates when `0` is received.
Here is the function which implements the child task: Here is the function that implements the child task:
~~~~ ~~~~
# import comm::{port, chan, methods}; # import comm::{port, chan, methods};