Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Soup up 'method syntax' chapter of the Book #21108

Merged
merged 1 commit into from
Jan 25, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 112 additions & 2 deletions src/doc/trpl/method-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ x.foo().bar().baz();
Luckily, as you may have guessed with the leading question, you can! Rust provides
the ability to use this *method call syntax* via the `impl` keyword.

## Method calls

Here's how it works:

```{rust}
Expand Down Expand Up @@ -56,11 +58,56 @@ other parameter. Because we know it's a `Circle`, we can access the `radius`
just like we would with any other struct. An import of π and some
multiplications later, and we have our area.

## 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', and we
can do it by returning `self`.

```
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) -> Circle {
Circle { x: self.x, y: self.y, radius: (self.radius * 10.0) }
}
}

fn main() {
let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
println!("{}", c.area());

let d = c.grow().area();
println!("{}", d);
}
```

Check the return type:

```
# struct Circle;
# impl Circle {
fn grow(&self) -> Circle {
# Circle } }
```

We just say we're returning a `Circle`. With this, we can grow a new circle
that's twice as big as the old one.

## Static methods

You can also define methods that do not take a `self` parameter. Here's a
pattern that's very common in Rust code:

```{rust}
# #![allow(non_shorthand_field_patterns)]
```
struct Circle {
x: f64,
y: f64,
Expand All @@ -86,3 +133,66 @@ This *static method* builds a new `Circle` for us. Note that static methods
are called with the `Struct::method()` syntax, rather than the `ref.method()`
syntax.

## Builder Pattern

Let's say that we want our users to be able to create Circles, but we will
allow them to only set the properties they care about. Otherwise, the `x`
and `y` attributes will be `0.0`, and the `radius` will be `1.0`. Rust doesn't
have method overloading, named arguments, or variable arguments. We employ
the builder pattern instead. It looks like this:

```
struct Circle {
x: f64,
y: f64,
radius: f64,
}

impl Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}

struct CircleBuilder {
coordinate: f64,
radius: f64,
}

impl CircleBuilder {
fn new() -> CircleBuilder {
CircleBuilder { coordinate: 0.0, radius: 0.0, }
}

fn coordinate(&mut self, coordinate: f64) -> &mut CircleBuilder {
self.coordinate = coordinate;
self
}

fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
self.radius = radius;
self
}

fn finalize(&self) -> Circle {
Circle { x: self.coordinate, y: self.coordinate, radius: self.radius }
}
}

fn main() {
let c = CircleBuilder::new()
.coordinate(10.0)
.radius(5.0)
.finalize();


println!("area: {}", c.area());
}
```

What we've done here is make another struct, `CircleBuilder`. We've defined our
builder methods on it. We've also defined our `area()` method on `Circle`. We
also made one more method on `CircleBuilder`: `finalize()`. This method creates
our final `Circle` from the builder. Now, we've 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.