Skip to content

Commit

Permalink
Copyedit sections 11-13 of the tutorial. That's all, folks!
Browse files Browse the repository at this point in the history
  • Loading branch information
catamorphism committed Oct 11, 2012
1 parent 6d25051 commit 39acb06
Showing 1 changed file with 103 additions and 85 deletions.
188 changes: 103 additions & 85 deletions doc/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -1807,10 +1807,13 @@ fn contains(v: &[int], elt: int) -> bool {
# Generics

Throughout this tutorial, we've been defining functions that act only on
specific data types. With type parameters we can also define functions whose
arguments represent generic types, and which can be invoked with a variety
of types. Consider a generic `map` function.
Throughout this tutorial, we've been defining functions that act only
on specific data types. With type parameters we can also define
functions whose arguments have generic types, and which can be invoked
with a variety of types. Consider a generic `map` function, which
takes a function `function` and a vector `vector` and returns a new
vector consisting of the result of applying `function` to each element
of `vector`:

~~~~
fn map<T, U>(vector: &[T], function: fn(v: &T) -> U) -> ~[U] {
Expand All @@ -1824,17 +1827,18 @@ fn map<T, U>(vector: &[T], function: fn(v: &T) -> U) -> ~[U] {

When defined with type parameters, as denoted by `<T, U>`, this
function can be applied to any type of vector, as long as the type of
`function`'s argument and the type of the vector's content agree with
`function`'s argument and the type of the vector's contents agree with
each other.

Inside a generic function, the names of the type parameters
(capitalized by convention) stand for opaque types. You can't look
inside them, but you can pass them around. Note that instances of
generic types are often passed by pointer. For example, the
parameter `function()` is supplied with a pointer to a value of type
`T` and not a value of type `T` itself. This ensures that the
function works with the broadest set of types possible, since some
types are expensive or illegal to copy and pass by value.
(capitalized by convention) stand for opaque types. All you can do
with instances of these types is pass them around: you can't apply any
operations to them or pattern-match on them. Note that instances of
generic types are often passed by pointer. For example, the parameter
`function()` is supplied with a pointer to a value of type `T` and not
a value of type `T` itself. This ensures that the function works with
the broadest set of types possible, since some types are expensive or
illegal to copy and pass by value.

Generic `type`, `struct`, and `enum` declarations follow the same pattern:

Expand All @@ -1852,15 +1856,16 @@ enum Maybe<T> {
}
~~~~

These declarations produce valid types like `Set<int>`, `Stack<int>`
and `Maybe<int>`.
These declarations can be instantiated to valid types like `Set<int>`,
`Stack<int>` and `Maybe<int>`.

Generic functions in Rust are compiled to very efficient runtime code
through a process called _monomorphisation_. This is a fancy way of
saying that, for each generic function you call, the compiler
generates a specialized version that is optimized specifically for the
argument types. In this respect Rust's generics have similar
performance characteristics to C++ templates.
The Rust compiler compiles generic functions very efficiently by
*monomorphizing* them. *Monomorphization* is a fancy name for a simple
idea: generate a separate copy of each generic function at each call
site where it is called, a copy that is specialized to the argument
types and can thus be optimized specifically for them. In this
respect, Rust's generics have similar performance characteristics to
C++ templates.

## Traits

Expand All @@ -1869,15 +1874,19 @@ are very limited. After all, since the function doesn't know what
types it is operating on, it can't safely modify or query their
values. This is where _traits_ come into play. Traits are Rust's most
powerful tool for writing polymorphic code. Java developers will see
in them aspects of Java interfaces, and Haskellers will notice their
similarities to type classes.

As motivation, let us consider copying in Rust. Perhaps surprisingly,
the copy operation is not defined for all Rust types. In
particular, types with user-defined destructors cannot be copied,
either implicitly or explicitly, and neither can types that own other
types containing destructors (the actual mechanism for defining
destructors will be discussed elsewhere).
them as similar to Java interfaces, and Haskellers will notice their
similarities to type classes. Rust's traits are a form of *bounded
polymorphism*: a trait is a way of limiting the set of possible types
that a type parameter could refer to.

As motivation, let us consider copying in Rust. The `copy` operation
is not defined for all Rust types. One reason is user-defined
destructors: copying a type that has a destructor could result in the
destructor running multiple times. Therefore, types with user-defined
destructors cannot be copied, either implicitly or explicitly, and
neither can types that own other types containing destructors (see the
section on [structs](#structs) for the actual mechanism for defining
destructors).

This complicates handling of generic functions. If you have a type
parameter `T`, can you copy values of that type? In Rust, you can't,
Expand All @@ -1890,8 +1899,8 @@ fn head_bad<T>(v: &[T]) -> T {
}
~~~~

We can tell the compiler though that the `head` function is only for
copyable types with the `Copy` trait.
However, we can tell the compiler that the `head` function is only for
copyable types: that is, those that have the `Copy` trait.

~~~~
// This does
Expand All @@ -1903,14 +1912,17 @@ fn head<T: Copy>(v: &[T]) -> T {
This says that we can call `head` on any type `T` as long as that type
implements the `Copy` trait. When instantiating a generic function,
you can only instantiate it with types that implement the correct
trait, so you could not apply `head` to a type with a destructor.
trait, so you could not apply `head` to a type with a
destructor. (`Copy` is a special trait that is built in to the
compiler, making it possible for the compiler to enforce this
restriction.)

While most traits can be defined and implemented by user code, three
traits are automatically derived and implemented for all applicable
types by the compiler, and may not be overridden:

* `Copy` - Types that can be copied, either implicitly, or using the
`copy` expression. All types are copyable unless they are classes
* `Copy` - Types that can be copied: either implicitly, or explicitly with the
`copy` operator. All types are copyable unless they are classes
with destructors or otherwise contain classes with destructors.

* `Send` - Sendable (owned) types. All types are sendable unless they
Expand Down Expand Up @@ -1957,7 +1969,7 @@ impl ~str: Printable {
# (~"foo").print();
~~~~

Methods defined in an implementation of a trait may be called just as
Methods defined in an implementation of a trait may be called just like
any other method, using dot notation, as in `1.print()`. Traits may
themselves contain type parameters. A trait for generalized sequence
types might look like the following:
Expand All @@ -1979,14 +1991,14 @@ impl<T> ~[T]: Seq<T> {
The implementation has to explicitly declare the type parameter that
it binds, `T`, before using it to specify its trait type. Rust
requires this declaration because the `impl` could also, for example,
specify an implementation of `Seq<int>`. The trait type -- appearing
after the colon in the `impl` -- *refers* to a type, rather than
specify an implementation of `Seq<int>`. The trait type (appearing
after the colon in the `impl`) *refers* to a type, rather than
defining one.

The type parameters bound by a trait are in scope in each of the
method declarations. So, re-declaring the type parameter
`T` as an explicit type parameter for `len` -- in either the trait or
the impl -- would be a compile-time error.
`T` as an explicit type parameter for `len`, in either the trait or
the impl, would be a compile-time error.

Within a trait definition, `self` is a special type that you can think
of as a type parameter. An implementation of the trait for any given
Expand All @@ -2006,16 +2018,17 @@ impl int: Eq {
}
~~~~

Notice that in the trait definition, `equals` takes a `self` type
argument, whereas, in the impl, `equals` takes an `int` type argument,
and uses `self` as the name of the receiver (analogous to the `this` pointer
in C++).
Notice that in the trait definition, `equals` takes a parameter of
type `self`. In contrast, in the `impl`, `equals` takes a parameter of
type `int`, and uses `self` as the name of the receiver (analogous to
the `this` pointer in C++).

## Bounded type parameters and static method dispatch

Traits give us a language for talking about the abstract capabilities
of types, and we can use this to place _bounds_ on type parameters,
so that we can then operate on generic types.
Traits give us a language for defining predicates on types, or
abstract properties that types can have. We can use this language to
define _bounds_ on type parameters, so that we can then operate on
generic types.

~~~~
# trait Printable { fn print(); }
Expand All @@ -2026,14 +2039,14 @@ fn print_all<T: Printable>(printable_things: ~[T]) {
}
~~~~

By declaring `T` as conforming to the `Printable` trait (as we earlier
did with `Copy`), it becomes possible to call methods from that trait
on values of that type inside the function. It will also cause a
Declaring `T` as conforming to the `Printable` trait (as we earlier
did with `Copy`) makes it possible to call methods from that trait
on values of type `T` inside the function. It will also cause a
compile-time error when anyone tries to call `print_all` on an array
whose element type does not have a `Printable` implementation.

Type parameters can have multiple bounds by separating them with spaces,
as in this version of `print_all` that makes copies of elements.
as in this version of `print_all` that copies elements.

~~~
# trait Printable { fn print(); }
Expand Down Expand Up @@ -2083,10 +2096,10 @@ fn draw_all(shapes: &[@Drawable]) {
}
~~~~

In this example there is no type parameter. Instead, the `@Drawable`
type is used to refer to any managed box value that implements the
`Drawable` trait. To construct such a value, you use the `as` operator
to cast a value to a trait type:
In this example, there is no type parameter. Instead, the `@Drawable`
type denotes any managed box value that implements the `Drawable`
trait. To construct such a value, you use the `as` operator to cast a
value to a trait type:

~~~~
# type Circle = int; type Rectangle = bool;
Expand All @@ -2104,10 +2117,12 @@ let r: @Rectangle = @new_rectangle();
draw_all(&[c as @Drawable, r as @Drawable]);
~~~~

Note that, like strings and vectors, trait types have dynamic size
and may only be used via one of the pointer types. In turn, the
`impl` is defined for `@Circle` and `@Rectangle` instead of for
just `Circle` and `Rectangle`. Other pointer types work as well.
We omit the code for `new_circle` and `new_rectangle`; imagine that

This comment has been minimized.

Copy link
@bstrie

bstrie Oct 11, 2012

Contributor

Rustdoc automatically checks the code examples for correctness when generating the docs. The lines preceded by # are just definitions necessary to get the code to compile, but get stripped out of the final versions of the generated docs. When reading the raw markdown files, feel free to gloss over those lines entirely.

This comment has been minimized.

Copy link
@bstrie

bstrie Oct 11, 2012

Contributor

@catamorphism It might even make sense to stick a line like this somewhere in the first code example:

# // If you're seeing this line, it means you're reading the unprocessed Markdown version of this tutorial. In this case, please disregard any lines in code examples that begin with a hash mark (#), as these are included only for automated correctness checking.

I know other people have been bitten by this in the past as well.

these just return `Circle`s and `Rectangle`s with a default size. Note
that, like strings and vectors, trait types have dynamic size and may
only be referred to via one of the pointer types. That's why the `impl` is
defined for `@Circle` and `@Rectangle` instead of for just `Circle`
and `Rectangle`. Other pointer types work as well.

~~~{.xfail-test}
# type Circle = int; type Rectangle = int;
Expand All @@ -2123,13 +2138,13 @@ let owny: ~Drawable = ~new_circle() as ~Drawable;
let stacky: &Drawable = &new_circle() as &Drawable;
~~~

> ***Note:*** Other pointer types actually _do not_ work here. This is
> ***Note:*** Other pointer types actually _do not_ work here yet. This is
> an evolving corner of the language.
Method calls to trait types are _dynamically dispatched_. Since the
compiler doesn't know specifically which functions to call at compile
time it uses a lookup table (vtable) to decide at runtime which
method to call.
time it uses a lookup table (also known as a vtable or dictionary) to
select the method to call at runtime.

This usage of traits is similar to Java interfaces.

Expand Down Expand Up @@ -2170,17 +2185,18 @@ fn chicken_farmer() {
~~~

These farm animal functions have a new keyword, `pub`, attached to
them. This is a visibility modifier that allows item to be accessed
outside of the module in which they are declared, using `::`, as in
`farm::chicken`. Items, like `fn`, `struct`, etc. are private by
default.
them. The `pub` keyword modifies an item's visibility, making it
visible outside its containing module. An expression with `::`, like
`farm::chicken`, can name an item outside of its containing
module. Items, such as those declared with `fn`, `struct`, `enum`,
`type`, or `const`, are module-private by default.

Visibility restrictions in Rust exist only at module boundaries. This
is quite different from most object-oriented languages that also enforce
restrictions on objects themselves. That's not to say that Rust doesn't
support encapsulation - both struct fields and methods can be private -
but it is at the module level, not the class level. Note that fields
and methods are _public_ by default.
is quite different from most object-oriented languages that also
enforce restrictions on objects themselves. That's not to say that
Rust doesn't support encapsulation: both struct fields and methods can
be private. But this encapsulation is at the module level, not the
struct level. Note that fields and methods are _public_ by default.

~~~
mod farm {
Expand Down Expand Up @@ -2220,7 +2236,7 @@ fn main() {

## Crates

The unit of independent compilation in Rust is the crate - rustc
The unit of independent compilation in Rust is the crate: rustc
compiles a single crate at a time, from which it produces either a
library or executable.

Expand Down Expand Up @@ -2294,38 +2310,40 @@ fn main() { bar::baz(); }
## Using other crates

Having compiled a crate into a library you can use it in another crate
with an `extern mod` directive. `extern mod` can appear at the top of
a crate file or at the top of modules. It will cause the compiler to
look in the library search path (which you can extend with `-L`
switch) for a compiled Rust library with the right name, then add a
module with that crate's name into the local scope.
The `extern mod` directive lets you use a crate (once it's been
compiled into a library) from inside another crate. `extern mod` can
appear at the top of a crate file or at the top of modules. It will
cause the compiler to look in the library search path (which you can
extend with the `-L` switch) for a compiled Rust library with the
right name, then add a module with that crate's name into the local
scope.

For example, `extern mod std` links the [standard library].

[standard library]: std/index.html

When a comma-separated list of name/value pairs is given after `extern
mod`, these are matched against the attributes provided in the `link`
attribute of the crate file, and a crate is only used when the two
match. A `name` value can be given to override the name used to search
for the crate.
When a comma-separated list of name/value pairs appears after `extern
mod`, the compiler front-end matches these pairs against the
attributes provided in the `link` attribute of the crate file. The
front-end will only select this crate for use if the actual pairs
match the declared attributes. You can provide a `name` value to
override the name used to search for the crate.

Our example crate declared this set of `link` attributes:

~~~~ {.xfail-test}
#[link(name = "farm", vers = "2.5", author = "mjh")];
~~~~

Which can then be linked with any (or all) of the following:
Which you can then link with any (or all) of the following:

~~~~ {.xfail-test}
extern mod farm;
extern mod my_farm (name = "farm", vers = "2.5");
extern mod my_auxiliary_farm (name = "farm", author = "mjh");
~~~~

If any of the requested metadata does not match then the crate
If any of the requested metadata do not match, then the crate
will not be compiled successfully.

## A minimal example
Expand Down Expand Up @@ -2361,7 +2379,7 @@ a hash representing the crate metadata.

## The core library

The Rust [core] library acts as the language runtime and contains
The Rust [core] library is the language runtime and contains
required memory management and task scheduling code as well as a
number of modules necessary for effective usage of the primitive
types. Methods on [vectors] and [strings], implementations of most
Expand Down

0 comments on commit 39acb06

Please sign in to comment.