2014-12-02 08:20:48 -06:00
|
|
|
|
% 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
|
2015-05-28 16:47:27 -05:00
|
|
|
|
baz(bar(foo));
|
2014-12-02 08:20:48 -06:00
|
|
|
|
```
|
|
|
|
|
|
2015-09-03 22:48:26 -05:00
|
|
|
|
We would read this left-to-right, and so we see ‘baz bar foo’. But this isn’t the
|
2015-04-18 15:01:28 -05:00
|
|
|
|
order that the functions would get called in, that’s inside-out: ‘foo bar baz’.
|
|
|
|
|
Wouldn’t it be nice if we could do this instead?
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-18 15:01:28 -05:00
|
|
|
|
```rust,ignore
|
2015-03-26 14:53:17 -05:00
|
|
|
|
foo.bar().baz();
|
2014-12-02 08:20:48 -06:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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.
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-23 10:35:37 -05:00
|
|
|
|
# Method calls
|
2015-01-13 14:00:49 -06:00
|
|
|
|
|
2015-04-18 15:01:28 -05:00
|
|
|
|
Here’s how it works:
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-18 15:01:28 -05:00
|
|
|
|
```rust
|
2014-12-02 08:20:48 -06:00
|
|
|
|
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
|
|
|
|
We’ve 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 it’s just a value on the stack,
|
|
|
|
|
`&self` if it’s a reference, and `&mut self` if it’s a mutable reference.
|
|
|
|
|
Because we took the `&self` parameter to `area`, we can use it just like any
|
|
|
|
|
other parameter. Because we know it’s 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. Here’s an
|
|
|
|
|
example of all three variants:
|
2015-03-08 09:49:13 -05:00
|
|
|
|
|
|
|
|
|
```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!");
|
2015-03-08 09:49:13 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn mutable_reference(&mut self) {
|
2015-03-14 18:09:26 -05:00
|
|
|
|
println!("taking self by mutable reference!");
|
2015-03-08 09:49:13 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn takes_ownership(self) {
|
2015-03-14 18:09:26 -05:00
|
|
|
|
println!("taking ownership of self!");
|
2015-03-08 09:49:13 -05:00
|
|
|
|
}
|
2015-03-08 23:46:34 -05:00
|
|
|
|
}
|
2015-03-08 09:49:13 -05:00
|
|
|
|
```
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-23 10:35:37 -05:00
|
|
|
|
# Chaining method calls
|
2015-01-13 14:00:49 -06:00
|
|
|
|
|
|
|
|
|
So, now we know how to call a method, such as `foo.bar()`. But what about our
|
2015-06-09 14:49:54 -05:00
|
|
|
|
original example, `foo.bar().baz()`? This is called ‘method chaining’. Let’s
|
|
|
|
|
look at an example:
|
2015-01-13 14:00:49 -06:00
|
|
|
|
|
2015-05-18 13:56:00 -05:00
|
|
|
|
```rust
|
2015-01-13 14:00:49 -06:00
|
|
|
|
struct Circle {
|
|
|
|
|
x: f64,
|
|
|
|
|
y: f64,
|
|
|
|
|
radius: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Circle {
|
|
|
|
|
fn area(&self) -> f64 {
|
|
|
|
|
std::f64::consts::PI * (self.radius * self.radius)
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-26 15:33:51 -05:00
|
|
|
|
fn grow(&self, increment: f64) -> Circle {
|
|
|
|
|
Circle { x: self.x, y: self.y, radius: self.radius + increment }
|
2015-01-13 14:00:49 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
|
|
|
|
|
println!("{}", c.area());
|
|
|
|
|
|
2015-03-26 15:33:51 -05:00
|
|
|
|
let d = c.grow(2.0).area();
|
2015-01-13 14:00:49 -06:00
|
|
|
|
println!("{}", d);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Check the return type:
|
|
|
|
|
|
2015-05-18 13:56:00 -05:00
|
|
|
|
```rust
|
2015-01-13 14:00:49 -06:00
|
|
|
|
# struct Circle;
|
|
|
|
|
# impl Circle {
|
2015-09-03 22:48:26 -05:00
|
|
|
|
fn grow(&self, increment: f64) -> Circle {
|
2015-01-13 14:00:49 -06:00
|
|
|
|
# Circle } }
|
|
|
|
|
```
|
|
|
|
|
|
2015-04-18 15:01:28 -05:00
|
|
|
|
We just say we’re 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-01-13 14:00:49 -06:00
|
|
|
|
|
2015-05-11 17:22:49 -05:00
|
|
|
|
# Associated functions
|
2015-01-13 14:00:49 -06:00
|
|
|
|
|
2015-05-11 17:22:49 -05:00
|
|
|
|
You can also define associated functions that do not take a `self` parameter.
|
|
|
|
|
Here’s a pattern that’s very common in Rust code:
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-05-11 17:22:49 -05:00
|
|
|
|
```rust
|
2014-12-02 08:20:48 -06:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2015-05-03 12:09:49 -05:00
|
|
|
|
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
|
2015-05-03 12:09:49 -05:00
|
|
|
|
methods’.
|
2014-12-02 08:20:48 -06:00
|
|
|
|
|
2015-04-23 10:35:37 -05:00
|
|
|
|
# Builder Pattern
|
2015-01-13 14:00:49 -06:00
|
|
|
|
|
2015-09-03 22:48:26 -05:00
|
|
|
|
Let’s say that we want our users to be able to create `Circle`s, but we will
|
2015-01-13 14:00:49 -06:00
|
|
|
|
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 doesn’t
|
2015-01-13 14:00:49 -06:00
|
|
|
|
have method overloading, named arguments, or variable arguments. We employ
|
|
|
|
|
the builder pattern instead. It looks like this:
|
|
|
|
|
|
2015-05-18 13:56:00 -05:00
|
|
|
|
```rust
|
2015-01-13 14:00:49 -06:00
|
|
|
|
struct Circle {
|
|
|
|
|
x: f64,
|
|
|
|
|
y: f64,
|
|
|
|
|
radius: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Circle {
|
|
|
|
|
fn area(&self) -> f64 {
|
|
|
|
|
std::f64::consts::PI * (self.radius * self.radius)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct CircleBuilder {
|
2015-03-26 18:30:09 -05:00
|
|
|
|
x: f64,
|
|
|
|
|
y: f64,
|
2015-01-13 14:00:49 -06:00
|
|
|
|
radius: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CircleBuilder {
|
|
|
|
|
fn new() -> CircleBuilder {
|
2015-05-08 09:39:25 -05:00
|
|
|
|
CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, }
|
2015-03-26 18:30:09 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
|
|
|
|
|
self.x = coordinate;
|
|
|
|
|
self
|
2015-01-13 14:00:49 -06:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-26 18:30:09 -05:00
|
|
|
|
fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
|
2015-04-03 13:17:15 -05:00
|
|
|
|
self.y = coordinate;
|
2015-03-14 14:27:44 -05:00
|
|
|
|
self
|
2015-01-13 14:00:49 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
|
2015-03-14 14:27:44 -05:00
|
|
|
|
self.radius = radius;
|
|
|
|
|
self
|
2015-01-13 14:00:49 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn finalize(&self) -> Circle {
|
2015-03-26 18:30:09 -05:00
|
|
|
|
Circle { x: self.x, y: self.y, radius: self.radius }
|
2015-01-13 14:00:49 -06:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let c = CircleBuilder::new()
|
2015-03-26 18:30:09 -05:00
|
|
|
|
.x(1.0)
|
|
|
|
|
.y(2.0)
|
|
|
|
|
.radius(2.0)
|
2015-01-13 14:00:49 -06:00
|
|
|
|
.finalize();
|
|
|
|
|
|
|
|
|
|
println!("area: {}", c.area());
|
2015-03-26 18:30:09 -05:00
|
|
|
|
println!("x: {}", c.x);
|
|
|
|
|
println!("y: {}", c.y);
|
2015-01-13 14:00:49 -06:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2015-09-03 22:48:26 -05:00
|
|
|
|
What we’ve done here is make another `struct`, `CircleBuilder`. We’ve defined our
|
2015-04-18 15:01:28 -05:00
|
|
|
|
builder methods on it. We’ve also defined our `area()` method on `Circle`. We
|
2015-01-13 14:00:49 -06:00
|
|
|
|
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, we’ve used the type system to enforce
|
2015-01-13 14:00:49 -06:00
|
|
|
|
our concerns: we can use the methods on `CircleBuilder` to constrain making
|
|
|
|
|
`Circle`s in any way we choose.
|