diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a38dc47..97044d6 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -22,7 +22,7 @@ - [Slices](slices.md) - [Structs](structs.md) - - [Method Syntax]() + - [Method Syntax](method-syntax.md) - [Generics]() - [Advanced]() diff --git a/src/method-syntax.md b/src/method-syntax.md new file mode 100644 index 0000000..0024208 --- /dev/null +++ b/src/method-syntax.md @@ -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. diff --git a/src/ownership.md b/src/ownership.md index 651ad7d..cfa7514 100644 --- a/src/ownership.md +++ b/src/ownership.md @@ -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: