tutorial: Rewrite method section to deal with explicit self

Has to be moved until after the pointer discussion.

/cc: #4217
This commit is contained in:
Brian Anderson 2012-12-20 01:34:15 -08:00
parent 7d2c2dbf4b
commit 171e6a16f7

View File

@ -918,7 +918,7 @@ match mytup {
}
~~~~
# Functions and methods
# Functions
We've already seen several function definitions. Like all other static
declarations, such as `type`, functions can be declared both at the
@ -968,52 +968,6 @@ assert 8 == line(5, 3, 1);
assert () == oops(5, 3, 1);
~~~~
Methods are like functions, except that they have an implicit argument
called `self`, which has the type that the method's receiver has. The
`self` argument is like 'this' in C++. An expression with dot
notation, as in `my_vec.len()`, denotes a method
call. Implementations, written with the `impl` keyword, can define
methods on most Rust types. As an example, let's define a `draw`
method on our `Shape` enum.
~~~
# fn draw_circle(p: Point, f: float) { }
# fn draw_rectangle(p: Point, p: Point) { }
struct Point {
x: float,
y: float
}
enum Shape {
Circle(Point, float),
Rectangle(Point, Point)
}
impl Shape {
fn draw() {
match self {
Circle(p, f) => draw_circle(p, f),
Rectangle(p1, p2) => draw_rectangle(p1, p2)
}
}
}
let s = Circle(Point { x: 1f, y: 2f }, 3f);
s.draw();
~~~
This defines an _implementation_ for `Shape` containing a single
method, `draw`. In most respects the `draw` method is defined
like any other function, except for the name `self`. `self`
is a special value that is automatically in scope inside each method,
referring to the value being operated on. If we wanted we could add
additional methods to the same impl, or multiple impls for the same
type. We'll discuss methods more in the context of [traits and
generics](#generics).
> ***Note:*** In the future, the method definition syntax will change to
> require declaring the `self` type explicitly, as the first argument.
# The Rust memory model
At this junction, let's take a detour to explain the concepts involved
@ -1526,6 +1480,115 @@ if favorite_crayon_name.len() > 5 {
}
~~~
# Methods
Methods are like functions except that they always begin with a special argument,
called `self`,
which has the type of the method's receiver. The
`self` argument is like `this` in C++ and many other languages.
Methods are called with dot notation, as in `my_vec.len()`.
_Implementations_, written with the `impl` keyword, can define
methods on most Rust types, including structs and enums.
As an example, let's define a `draw` method on our `Shape` enum.
~~~
# fn draw_circle(p: Point, f: float) { }
# fn draw_rectangle(p: Point, p: Point) { }
struct Point {
x: float,
y: float
}
enum Shape {
Circle(Point, float),
Rectangle(Point, Point)
}
impl Shape {
fn draw(&self) {
match *self {
Circle(p, f) => draw_circle(p, f),
Rectangle(p1, p2) => draw_rectangle(p1, p2)
}
}
}
let s = Circle(Point { x: 1f, y: 2f }, 3f);
s.draw();
~~~
This defines an _implementation_ for `Shape` containing a single
method, `draw`. In most respects the `draw` method is defined
like any other function, except for the name `self`.
The type of `self` is the type on which the method is implemented,
or a pointer thereof. As an argument it is written either `self`,
`&self`, `@self`, or `~self`.
A caller must in turn have a compatible pointer type to call the method.
~~~
# fn draw_circle(p: Point, f: float) { }
# fn draw_rectangle(p: Point, p: Point) { }
# struct Point { x: float, y: float }
# enum Shape {
# Circle(Point, float),
# Rectangle(Point, Point)
# }
impl Shape {
fn draw_borrowed(&self) { ... }
fn draw_managed(@self) { ... }
fn draw_owned(~self) { ... }
fn draw_value(self) { ... }
}
let s = Circle(Point { x: 1f, y: 2f }, 3f);
(@s).draw_managed();
(~s).draw_owned();
(&s).draw_borrowed();
s.draw_value();
~~~
Methods typically take a borrowed pointer self type,
so the compiler will go to great lengths to convert a callee
to a borrowed pointer.
~~~
# fn draw_circle(p: Point, f: float) { }
# fn draw_rectangle(p: Point, p: Point) { }
# struct Point { x: float, y: float }
# enum Shape {
# Circle(Point, float),
# Rectangle(Point, Point)
# }
# impl Shape {
# fn draw_borrowed(&self) { ... }
# fn draw_managed(@self) { ... }
# fn draw_owned(~self) { ... }
# fn draw_value(self) { ... }
# }
# let s = Circle(Point { x: 1f, y: 2f }, 3f);
// As with typical function arguments, managed and unique pointers
// are automatically converted to borrowed pointers
(@s).draw_borrowed();
(~s).draw_borrowed();
// Unlike typical function arguments, the self value will
// automatically be referenced ...
s.draw_borrowed();
// ... and dereferenced
(& &s).draw_borrowed();
// ... and dereferenced, and borrowed, and
(&@~s).draw_borrowed();
~~~
We'll discuss implementations more in the context of [traits and
generics](#generics).
# Closures
Named functions, like those we've seen so far, may not refer to local