TRPL edits: method syntax
This commit is contained in:
parent
7f43c5782c
commit
f81b1fcf8c
@ -3,27 +3,26 @@
|
||||
Functions are great, but if you want to call a bunch of them on some data, it
|
||||
can be awkward. Consider this code:
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
baz(bar(foo)));
|
||||
```
|
||||
|
||||
We would read this left-to right, and so we see "baz bar foo." But this isn't the
|
||||
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?
|
||||
We would read this left-to right, and so we see ‘baz bar foo’. But this isn’t the
|
||||
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?
|
||||
|
||||
```{rust,ignore}
|
||||
```rust,ignore
|
||||
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.
|
||||
the ability to use this ‘method call syntax’ via the `impl` keyword.
|
||||
|
||||
## Method calls
|
||||
|
||||
Here's how it works:
|
||||
Here’s how it works:
|
||||
|
||||
```{rust}
|
||||
# #![feature(core)]
|
||||
```rust
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
@ -44,15 +43,23 @@ fn main() {
|
||||
|
||||
This will print `12.566371`.
|
||||
|
||||
We've made a struct that represents a circle. We then write an `impl` block,
|
||||
and inside it, define a method, `area`. Methods take a special first
|
||||
parameter, of which there are three variants: `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. 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:
|
||||
|
||||
|
||||
We’ve made a struct that represents a circle. We then write an `impl` block,
|
||||
and inside it, define a method, `area`.
|
||||
|
||||
Methods take a special first parameter, of which there are three variants:
|
||||
`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`
|
||||
just like we would with any other struct.
|
||||
|
||||
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:
|
||||
|
||||
```rust
|
||||
struct Circle {
|
||||
@ -76,20 +83,13 @@ impl Circle {
|
||||
}
|
||||
```
|
||||
|
||||
Finally, as you may remember, the value of the area of a circle is `π*r²`.
|
||||
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`
|
||||
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
|
||||
original example, `foo.bar().baz()`? This is called ‘method chaining’, and we
|
||||
can do it by returning `self`.
|
||||
|
||||
```
|
||||
# #![feature(core)]
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
@ -124,13 +124,13 @@ fn grow(&self) -> Circle {
|
||||
# Circle } }
|
||||
```
|
||||
|
||||
We just say we're returning a `Circle`. With this method, we can grow a new
|
||||
We just say we’re returning a `Circle`. With this method, we can grow a new
|
||||
circle to any arbitrary size.
|
||||
|
||||
## 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:
|
||||
You can also define methods that do not take a `self` parameter. Here’s a
|
||||
pattern that’s very common in Rust code:
|
||||
|
||||
```
|
||||
struct Circle {
|
||||
@ -154,20 +154,19 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
This *static method* builds a new `Circle` for us. Note that static methods
|
||||
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
|
||||
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
|
||||
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:
|
||||
|
||||
```
|
||||
# #![feature(core)]
|
||||
struct Circle {
|
||||
x: f64,
|
||||
y: f64,
|
||||
@ -224,9 +223,9 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
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 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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user