rust/src/doc/trpl/method-syntax.md

233 lines
5.5 KiB
Markdown
Raw Normal View History

% Method Syntax
Functions are great, but if you want to call a bunch of them on some data, it
can be awkward. Consider this code:
2015-04-18 15:01:28 -05:00
```rust,ignore
baz(bar(foo));
```
2015-09-03 22:48:26 -05:00
We would read this left-to-right, and so we see baz bar foo. But this isnt the
2015-04-18 15:01:28 -05:00
order that the functions would get called in, thats inside-out: foo bar baz.
Wouldnt it be nice if we could do this instead?
2015-04-18 15:01:28 -05:00
```rust,ignore
foo.bar().baz();
```
Luckily, as you may have guessed with the leading question, you can! Rust provides
2015-04-18 15:01:28 -05:00
the ability to use this method call syntax via the `impl` keyword.
2015-04-23 10:35:37 -05:00
# Method calls
2015-04-18 15:01:28 -05:00
Heres how it works:
2015-04-18 15:01:28 -05:00
```rust
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
fn main() {
let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
println!("{}", c.area());
}
```
This will print `12.566371`.
2015-04-18 15:01:28 -05:00
2015-09-03 22:48:26 -05:00
Weve made a `struct` that represents a circle. We then write an `impl` block,
2015-04-18 15:01:28 -05:00
and inside it, define a method, `area`.
2015-09-03 22:48:26 -05:00
Methods take a special first parameter, of which there are three variants:
2015-04-18 15:01:28 -05:00
`self`, `&self`, and `&mut self`. You can think of this first parameter as
being the `foo` in `foo.bar()`. The three variants correspond to the three
kinds of things `foo` could be: `self` if its just a value on the stack,
`&self` if its a reference, and `&mut self` if its a mutable reference.
Because we took the `&self` parameter to `area`, we can use it just like any
other parameter. Because we know its a `Circle`, we can access the `radius`
2015-09-03 22:48:26 -05:00
just like we would with any other `struct`.
2015-04-18 15:01:28 -05:00
We should default to using `&self`, as you should prefer borrowing over taking
ownership, as well as taking immutable references over mutable ones. Heres an
example of all three variants:
```rust
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn reference(&self) {
2015-03-14 18:09:26 -05:00
println!("taking self by reference!");
}
fn mutable_reference(&mut self) {
2015-03-14 18:09:26 -05:00
println!("taking self by mutable reference!");
}
fn takes_ownership(self) {
2015-03-14 18:09:26 -05:00
println!("taking ownership of self!");
}
2015-03-08 23:46:34 -05:00
}
```
2015-04-23 10:35:37 -05:00
# Chaining method calls
So, now we know how to call a method, such as `foo.bar()`. But what about our
original example, `foo.bar().baz()`? This is called method chaining. Lets
look at an example:
```rust
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
fn grow(&self, increment: f64) -> Circle {
Circle { x: self.x, y: self.y, radius: self.radius + increment }
}
}
fn main() {
let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
println!("{}", c.area());
let d = c.grow(2.0).area();
println!("{}", d);
}
```
Check the return type:
```rust
# struct Circle;
# impl Circle {
2015-09-03 22:48:26 -05:00
fn grow(&self, increment: f64) -> Circle {
# Circle } }
```
2015-04-18 15:01:28 -05:00
We just say were returning a `Circle`. With this method, we can grow a new
2015-09-03 22:51:08 -05:00
`Circle` to any arbitrary size.
2015-05-11 17:22:49 -05:00
# Associated functions
2015-05-11 17:22:49 -05:00
You can also define associated functions that do not take a `self` parameter.
Heres a pattern thats very common in Rust code:
2015-05-11 17:22:49 -05:00
```rust
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn new(x: f64, y: f64, radius: f64) -> Circle {
Circle {
x: x,
y: y,
radius: radius,
}
}
}
fn main() {
let c = Circle::new(0.0, 0.0, 2.0);
}
```
This associated function builds a new `Circle` for us. Note that associated
functions are called with the `Struct::function()` syntax, rather than the
2015-05-15 13:46:59 -05:00
`ref.method()` syntax. Some other languages call associated functions static
methods.
2015-04-23 10:35:37 -05:00
# Builder Pattern
2015-09-03 22:48:26 -05:00
Lets say that we want our users to be able to create `Circle`s, but we will
allow them to only set the properties they care about. Otherwise, the `x`
2015-04-18 15:01:28 -05:00
and `y` attributes will be `0.0`, and the `radius` will be `1.0`. Rust doesnt
have method overloading, named arguments, or variable arguments. We employ
the builder pattern instead. It looks like this:
```rust
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
struct CircleBuilder {
x: f64,
y: f64,
radius: f64,
}
impl CircleBuilder {
fn new() -> CircleBuilder {
CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, }
}
fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
self.x = coordinate;
self
}
fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
2015-04-03 13:17:15 -05:00
self.y = coordinate;
self
}
fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
self.radius = radius;
self
}
fn finalize(&self) -> Circle {
Circle { x: self.x, y: self.y, radius: self.radius }
}
}
fn main() {
let c = CircleBuilder::new()
.x(1.0)
.y(2.0)
.radius(2.0)
.finalize();
println!("area: {}", c.area());
println!("x: {}", c.x);
println!("y: {}", c.y);
}
```
2015-09-03 22:48:26 -05:00
What weve done here is make another `struct`, `CircleBuilder`. Weve defined our
2015-04-18 15:01:28 -05:00
builder methods on it. Weve also defined our `area()` method on `Circle`. We
also made one more method on `CircleBuilder`: `finalize()`. This method creates
2015-04-18 15:01:28 -05:00
our final `Circle` from the builder. Now, weve used the type system to enforce
our concerns: we can use the methods on `CircleBuilder` to constrain making
`Circle`s in any way we choose.