Skip to content

Commit 1b1aea8

Browse files
committed
tutorial: Begin trying to improve the generics section
1 parent 8baed1f commit 1b1aea8

File tree

1 file changed

+76
-54
lines changed

1 file changed

+76
-54
lines changed

doc/tutorial.md

Lines changed: 76 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,12 +1528,9 @@ fn contains(v: &[int], elt: int) -> bool {
15281528

15291529
# Generics
15301530

1531-
## Generic functions
1532-
15331531
Throughout this tutorial, we've been defining functions that act only on
1534-
single data types. It's a burden to define such functions again and again for
1535-
every type they apply to. Thus, Rust allows functions and datatypes to have
1536-
type parameters.
1532+
single data types. With type parameters we can also define functions that
1533+
may be invoked on multiple types.
15371534

15381535
~~~~
15391536
fn map<T, U>(vector: &[T], function: fn(v: &T) -> U) -> ~[U] {
@@ -1545,9 +1542,10 @@ fn map<T, U>(vector: &[T], function: fn(v: &T) -> U) -> ~[U] {
15451542
}
15461543
~~~~
15471544

1548-
When defined with type parameters, this function can be applied to any
1549-
type of vector, as long as the type of `function`'s argument and the
1550-
type of the vector's content agree with each other.
1545+
When defined with type parameters, as denoted by `<T, U>`, this
1546+
function can be applied to any type of vector, as long as the type of
1547+
`function`'s argument and the type of the vector's content agree with
1548+
each other.
15511549

15521550
Inside a generic function, the names of the type parameters
15531551
(capitalized by convention) stand for opaque types. You can't look
@@ -1558,11 +1556,12 @@ parameter `function()` is supplied with a pointer to a value of type
15581556
function works with the broadest set of types possible, since some
15591557
types are expensive or illegal to copy and pass by value.
15601558

1561-
## Generic datatypes
1562-
15631559
Generic `type`, `struct`, and `enum` declarations follow the same pattern:
15641560

15651561
~~~~
1562+
# use std::map::HashMap;
1563+
type Set<T> = HashMap<T, ()>;
1564+
15661565
struct Stack<T> {
15671566
elements: ~[mut T]
15681567
}
@@ -1573,89 +1572,112 @@ enum Maybe<T> {
15731572
}
15741573
~~~~
15751574

1576-
These declarations produce valid types like `Stack<u8>` and `Maybe<int>`.
1575+
These declarations produce valid types like `Set<int>`, `Stack<int>`
1576+
and `Maybe<int>`.
15771577

1578-
## Kinds
1578+
## Traits
15791579

15801580
Perhaps surprisingly, the 'copy' (duplicate) operation is not defined
1581-
for all Rust types. Resource types (classes with destructors) cannot be
1582-
copied, and neither can any type whose copying would require copying a
1583-
resource (such as records or unique boxes containing a resource).
1581+
for all Rust types. Types with user-defined destructors cannot be
1582+
copied, and neither can types that own other types containing
1583+
destructors.
1584+
1585+
~~~
1586+
// Instances of this struct can't be copied, either implicitly
1587+
// or with the `copy` keyword
1588+
struct NotCopyable {
1589+
foo: int,
1590+
1591+
drop { }
1592+
}
1593+
1594+
// This owned box containing a NotCopyable is also not copyable
1595+
let not_copyable_box = ~NotCopyable { foo: 0 };
1596+
~~~
15841597

15851598
This complicates handling of generic functions. If you have a type
15861599
parameter `T`, can you copy values of that type? In Rust, you can't,
1587-
unless you explicitly declare that type parameter to have copyable
1588-
'kind'. A kind is a type of type.
1600+
unless you explicitly declare that type parameter to have the
1601+
_trait_ for copying, called `Copy`.
15891602

15901603
~~~~ {.ignore}
15911604
// This does not compile
1592-
fn head_bad<T>(v: ~[T]) -> T { v[0] }
1605+
fn head_bad<T>(v: ~[T]) -> T {
1606+
copy v[0] // Elements of type T aren't copyable
1607+
}
1608+
~~~~
1609+
1610+
~~~~
15931611
// This does
1594-
fn head<T: Copy>(v: ~[T]) -> T { v[0] }
1612+
fn head<T: Copy>(v: ~[T]) -> T {
1613+
copy v[0]
1614+
}
15951615
~~~~
15961616

15971617
When instantiating a generic function, you can only instantiate it
1598-
with types that fit its kinds. So you could not apply `head` to a
1599-
resource type. Rust has several kinds that can be used as type bounds:
1600-
1601-
* `Copy` - Copyable types. All types are copyable unless they
1602-
are classes with destructors or otherwise contain
1603-
classes with destructors.
1604-
* `Send` - Sendable types. All types are sendable unless they
1605-
contain shared boxes, closures, or other local-heap-allocated
1606-
types.
1607-
* `Const` - Constant types. These are types that do not contain
1608-
mutable fields nor shared boxes.
1609-
1610-
> ***Note:*** Rust type kinds are syntactically very similar to
1611-
> [traits](#traits) when used as type bounds, and can be
1612-
> conveniently thought of as built-in traits. In the future type
1613-
> kinds will actually be traits that the compiler has special
1614-
> knowledge about.
1615-
1616-
# Traits
1618+
with types that implement the correct traits. So you could not apply
1619+
`head` to a type with a destructor.
1620+
1621+
While most traits can be defined and implemented by user code, three
1622+
traits are derived for all applicable types by the compiler, and may
1623+
not be overridden:
1624+
1625+
* `Copy` - Types that can be copied, either implicitly, or using the
1626+
`copy` expression. All types are copyable unless they are classes
1627+
with destructors or otherwise contain classes with destructors.
1628+
1629+
* `Send` - Sendable (owned) types. All types are sendable unless they
1630+
contain managed boxes, managed closures, or otherwise managed
1631+
types. Sendable types may or may not be copyable.
1632+
1633+
* `Const` - Constant (immutable) types. These are types that do not contain
1634+
mutable fields.
1635+
1636+
> ***Note:*** These three traits were referred to as 'kinds' in earlier
1637+
> iterations of the language, and often still are.
16171638
16181639
Traits are Rust's take on value polymorphism—the thing that
16191640
object-oriented languages tend to solve with methods and inheritance.
16201641
For example, writing a function that can operate on multiple types of
16211642
collections.
16221643

1623-
> ***Note:*** This feature is very new, and will need a few extensions to be
1624-
> applicable to more advanced use cases.
1625-
1626-
## Declaration
1644+
## Declaring and implementing traits
16271645

1628-
A trait consists of a set of methods. A method is a function that
1646+
A trait consists of a set of methods, or may be empty, as is the case
1647+
with `Copy`, `Send`, and `Const`. A method is a function that
16291648
can be applied to a `self` value and a number of arguments, using the
16301649
dot notation: `self.foo(arg1, arg2)`.
16311650

1632-
For example, we could declare the trait `to_str` for things that
1633-
can be converted to a string, with a single method of the same name:
1651+
For example, we could declare the trait `Stringable` for things that
1652+
can be converted to a string, with a single method:
16341653

16351654
~~~~
16361655
trait ToStr {
1637-
fn to_str() -> ~str;
1656+
fn to_str(self) -> ~str;
16381657
}
16391658
~~~~
16401659

1641-
## Implementation
1642-
16431660
To actually implement a trait for a given type, the `impl` form
1644-
is used. This defines implementations of `to_str` for the `int` and
1661+
is used. This defines implementations of `ToStr` for the `int` and
16451662
`~str` types.
16461663

16471664
~~~~
1648-
# trait ToStr { fn to_str() -> ~str; }
1665+
# // FIXME: This example is no good because you can't actually
1666+
# // implement your own .to_str for int and ~str
1667+
# trait ToStr { fn to_str(self) -> ~str; }
16491668
impl int: ToStr {
1650-
fn to_str() -> ~str { int::to_str(self, 10u) }
1669+
fn to_str(self) -> ~str { int::to_str(self, 10u) }
16511670
}
16521671
impl ~str: ToStr {
1653-
fn to_str() -> ~str { self }
1672+
fn to_str(self) -> ~str { self }
16541673
}
1674+
1675+
# //1.to_str();
1676+
# //(~"foo").to_str();
16551677
~~~~
16561678

1657-
Given these, we may call `1.to_str()` to get `~"1"`, or
1658-
`(~"foo").to_str()` to get `~"foo"` again. This is basically a form of
1679+
Given these, we may call `1.to_str()` to get `"1"`, or
1680+
`(~"foo").to_str()` to get `"foo"` again. This is basically a form of
16591681
static overloading—when the Rust compiler sees the `to_str` method
16601682
call, it looks for an implementation that matches the type with a
16611683
method that matches the name, and simply calls that.

0 commit comments

Comments
 (0)