mirror of
https://github.com/rust-lang-cn/book-cn.git
synced 2025-02-02 23:38:41 +08:00
Merge pull request #70 from rust-lang/method_syntax
First draft: method syntax
This commit is contained in:
commit
61194c7b8f
@ -22,7 +22,7 @@
|
||||
- [Slices](slices.md)
|
||||
|
||||
- [Structs](structs.md)
|
||||
- [Method Syntax]()
|
||||
- [Method Syntax](method-syntax.md)
|
||||
- [Generics]()
|
||||
- [Advanced]()
|
||||
|
||||
|
277
src/method-syntax.md
Normal file
277
src/method-syntax.md
Normal file
@ -0,0 +1,277 @@
|
||||
# Method Syntax
|
||||
|
||||
In the last section on ownership, we made several references to ‘methods’.
|
||||
Methods look like this:
|
||||
|
||||
```rust
|
||||
let s1 = String::from("hello");
|
||||
|
||||
// call a method on our String
|
||||
let s2 = s1.clone();
|
||||
|
||||
println!("{}", s1);
|
||||
```
|
||||
|
||||
The call to `clone()` is attatched to `s1` with a dot. This is called ‘method
|
||||
syntax’, and it’s a way to call certain functions with a different style.
|
||||
|
||||
Why have two ways to call functions? We’ll talk about some deeper reasons
|
||||
related to ownership in a moment, but one big reason is that methods look nicer
|
||||
when chained together:
|
||||
|
||||
```rust,ignore
|
||||
// with functions
|
||||
h(g(f(x)));
|
||||
|
||||
// with methods
|
||||
x.f().g().h();
|
||||
```
|
||||
|
||||
The nested-functions version reads in reverse: we call `f()`, then `g()`, then
|
||||
`h()`, but it reads as `h()`, then `g()`, then `f()`.
|
||||
|
||||
Before we get into the details, let’s talk about how to define your own
|
||||
methods.
|
||||
|
||||
## Defining methods
|
||||
|
||||
We can define methods with the `impl` keyword. `impl` is short for
|
||||
‘implementation’. Doing so looks like this:
|
||||
|
||||
```rust
|
||||
#[derive(Debug,Copy,Clone)]
|
||||
struct Point {
|
||||
x: f64,
|
||||
y: f64,
|
||||
}
|
||||
|
||||
impl Point {
|
||||
fn distance(&self, other: &Point) -> f64 {
|
||||
let x_squared = f64::powi(other.x - self.x, 2);
|
||||
let y_squared = f64::powi(other.y - self.y, 2);
|
||||
|
||||
f64::sqrt(x_squared + y_squared)
|
||||
}
|
||||
}
|
||||
|
||||
let p1 = Point { x: 0.0, y: 0.0 };
|
||||
let p2 = Point { x: 5.0, y: 6.5 };
|
||||
|
||||
assert_eq!(8.200609733428363, p1.distance(&p2));
|
||||
```
|
||||
|
||||
Let’s break this down. First, we have our `Point` struct from earlier in the
|
||||
chapter. Next comes our first use of the `impl` keyword:
|
||||
|
||||
```
|
||||
# #[derive(Debug,Copy,Clone)]
|
||||
# struct Point {
|
||||
# x: f64,
|
||||
# y: f64,
|
||||
# }
|
||||
#
|
||||
impl Point {
|
||||
# fn distance(&self, other: &Point) -> f64 {
|
||||
# let x_squared = f64::powi(other.x - self.x, 2);
|
||||
# let y_squared = f64::powi(other.y - self.y, 2);
|
||||
#
|
||||
# f64::sqrt(x_squared + y_squared)
|
||||
# }
|
||||
}
|
||||
#
|
||||
# let p1 = Point { x: 0.0, y: 0.0 };
|
||||
# let p2 = Point { x: 5.0, y: 6.5 };
|
||||
#
|
||||
# assert_eq!(8.200609733428363, p1.distance(&p2));
|
||||
```
|
||||
|
||||
Everything we put inside of the curly braces will be methods implemented on
|
||||
`Point`.
|
||||
|
||||
```
|
||||
# #[derive(Debug,Copy,Clone)]
|
||||
# struct Point {
|
||||
# x: f64,
|
||||
# y: f64,
|
||||
# }
|
||||
#
|
||||
# impl Point {
|
||||
fn distance(&self, other: &Point) -> f64 {
|
||||
# let x_squared = f64::powi(other.x - self.x, 2);
|
||||
# let y_squared = f64::powi(other.y - self.y, 2);
|
||||
#
|
||||
# f64::sqrt(x_squared + y_squared)
|
||||
}
|
||||
# }
|
||||
#
|
||||
# let p1 = Point { x: 0.0, y: 0.0 };
|
||||
# let p2 = Point { x: 5.0, y: 6.5 };
|
||||
#
|
||||
# assert_eq!(8.200609733428363, p1.distance(&p2));
|
||||
```
|
||||
|
||||
Next is our definition. This looks very similar to our previous definition of
|
||||
`distance()` as a function:
|
||||
|
||||
```rust
|
||||
# #[derive(Debug,Copy,Clone)]
|
||||
# struct Point {
|
||||
# x: f64,
|
||||
# y: f64,
|
||||
# }
|
||||
fn distance(p1: Point, p2: Point) -> f64 {
|
||||
# let x_squared = f64::powi(p2.x - p1.x, 2);
|
||||
# let y_squared = f64::powi(p2.y - p1.y, 2);
|
||||
#
|
||||
# f64::sqrt(x_squared + y_squared)
|
||||
# }
|
||||
```
|
||||
|
||||
Other than this, the rest of the example is familliar: an implementation of
|
||||
`distance()`, and using the method to find an answer.
|
||||
|
||||
There are two differences. The first is in the first argument. Instead of a name
|
||||
and a type, we have written `&self`. This is what distinguishes a method from a
|
||||
function: using `self` inside of an `impl` block. Because we already know that
|
||||
we are implementing this method on `Point`, we don’t need to write the type of
|
||||
`self` out. However, we have written `&self`, not only `self`. This is because
|
||||
we want to take our argument by reference rather than by ownership. In other
|
||||
words, these two forms are the same:
|
||||
|
||||
```rust,ignore
|
||||
fn foo(self: &Point)
|
||||
fn foo(&self)
|
||||
```
|
||||
|
||||
Just like any other parameter, you can take `self` in three forms. Here’s the
|
||||
list, with the most common form first:
|
||||
|
||||
```rust,ignore
|
||||
fn foo(&self) // take self by reference
|
||||
fn foo(&mut self) // take self by mutable reference
|
||||
fn foo(self) // take self by ownership
|
||||
```
|
||||
|
||||
In this case, we only need a reference. We don’t plan on taking ownership, and
|
||||
we don’t need to mutate either point. Taking by reference is by far the most
|
||||
common form of method, followed by a mutable reference, and then occasionally
|
||||
by ownership.
|
||||
|
||||
### Methods and automatic referencing
|
||||
|
||||
We’ve left out an important detail. It’s in this line of the example:
|
||||
|
||||
```
|
||||
# #[derive(Debug,Copy,Clone)]
|
||||
# struct Point {
|
||||
# x: f64,
|
||||
# y: f64,
|
||||
# }
|
||||
#
|
||||
# impl Point {
|
||||
# fn distance(&self, other: &Point) -> f64 {
|
||||
# let x_squared = f64::powi(other.x - self.x, 2);
|
||||
# let y_squared = f64::powi(other.y - self.y, 2);
|
||||
#
|
||||
# f64::sqrt(x_squared + y_squared)
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# let p1 = Point { x: 0.0, y: 0.0 };
|
||||
# let p2 = Point { x: 5.0, y: 6.5 };
|
||||
#
|
||||
assert_eq!(8.200609733428363, p1.distance(&p2));
|
||||
```
|
||||
|
||||
When we defined `distance()`, we took both `self` and the other argument by
|
||||
reference. Yet, we needed a `&` for `p2` but not `p1`. What gives?
|
||||
|
||||
This feature is called ‘automatic referencing’, and calling methods is one
|
||||
of the few places in Rust that has behavior like this. Here’s how it works:
|
||||
when you call a method with `self.(`, Rust will automatically add in `&`s
|
||||
or `&mut`s to match the signature. In other words, these three are the same:
|
||||
|
||||
```rust
|
||||
# #[derive(Debug,Copy,Clone)]
|
||||
# struct Point {
|
||||
# x: f64,
|
||||
# y: f64,
|
||||
# }
|
||||
#
|
||||
# impl Point {
|
||||
# fn distance(&self, other: &Point) -> f64 {
|
||||
# let x_squared = f64::powi(other.x - self.x, 2);
|
||||
# let y_squared = f64::powi(other.y - self.y, 2);
|
||||
#
|
||||
# f64::sqrt(x_squared + y_squared)
|
||||
# }
|
||||
# }
|
||||
# let p1 = Point { x: 0.0, y: 0.0 };
|
||||
# let p2 = Point { x: 5.0, y: 6.5 };
|
||||
p1.distance(&p2);
|
||||
(&p1).distance(&p2);
|
||||
Point::distance(&p1, &p2);
|
||||
```
|
||||
|
||||
The first one looks much, much cleaner. Here’s another example:
|
||||
|
||||
```rust
|
||||
let mut s = String::from("Hello,");
|
||||
|
||||
s.push_str(" world!");
|
||||
|
||||
// The above is the same as:
|
||||
// (&mut s).push_str(" world!");
|
||||
|
||||
assert_eq!("Hello, world!", s);
|
||||
```
|
||||
|
||||
Because [`push_str()`] has the following signature:
|
||||
|
||||
```rust,ignore
|
||||
fn push_str(&mut self, string: &str) {
|
||||
```
|
||||
|
||||
[`push_str()`]: http://doc.rust-lang.org/collections/string/struct.String.html#method.push_str
|
||||
|
||||
This automatic referencing behavior works because methods have a clear receiver
|
||||
— the type of `self` — and in most cases it’s clear given the receiver and name
|
||||
of a method whether the method is just reading (so needs `&self`), mutating (so
|
||||
`&mut self`), or consuming (so `self`). The fact that Rust makes borrowing
|
||||
implicit for method receivers is a big part of making ownership ergonomic in
|
||||
practice.
|
||||
|
||||
## Methods can be called like functions
|
||||
|
||||
Furthermore, if we have a method, we can also call it like a function:
|
||||
|
||||
```rust
|
||||
# #[derive(Debug,Copy,Clone)]
|
||||
# struct Point {
|
||||
# x: f64,
|
||||
# y: f64,
|
||||
# }
|
||||
#
|
||||
# impl Point {
|
||||
# fn distance(&self, other: &Point) -> f64 {
|
||||
# let x_squared = f64::powi(other.x - self.x, 2);
|
||||
# let y_squared = f64::powi(other.y - self.y, 2);
|
||||
#
|
||||
# f64::sqrt(x_squared + y_squared)
|
||||
# }
|
||||
# }
|
||||
# let p1 = Point { x: 0.0, y: 0.0 };
|
||||
# let p2 = Point { x: 5.0, y: 6.5 };
|
||||
let d1 = p1.distance(&p2);
|
||||
let d2 = Point::distance(&p1, &p2);
|
||||
|
||||
assert_eq!(d1, d2);
|
||||
```
|
||||
|
||||
Instead of using `self.(`, we use `Point` and the namespace operator to call it
|
||||
like a function instead. Because functions do not do the automatic referencing,
|
||||
we must pass in `&p1` explicitly.
|
||||
|
||||
While methods can be called like functions, functions cannot be called like
|
||||
methods. If the first argument isn’t named `self`, it cannot be called like a
|
||||
method.
|
@ -257,8 +257,11 @@ inexpensive.
|
||||
## Clone
|
||||
|
||||
But what if we _do_ want to deeply copy the `String`’s data, and not just the
|
||||
`String` itself? There’s a common method for that: `clone()`. Here’s an example
|
||||
of `clone()` in action:
|
||||
`String` itself? There’s a common method for that: `clone()`. We will discuss
|
||||
methods in the next section on [`struct`]s, but they’re a common enough feature
|
||||
in many programming languages that you have probably seen them before.
|
||||
|
||||
Here’s an example of the `clone()` method in action:
|
||||
|
||||
```rust
|
||||
let s1 = String::from("hello");
|
||||
@ -267,6 +270,8 @@ let s2 = s1.clone();
|
||||
println!("{}", s1);
|
||||
```
|
||||
|
||||
[`struct`]: structs.html
|
||||
|
||||
This will work just fine. Remember our diagram from before? In this case,
|
||||
it _is_ doing this:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user