3 Functions are great, but if you want to call a bunch of them on some data, it
4 can be awkward. Consider this code:
10 We would read this left-to right, and so we see "baz bar foo." But this isn't the
11 order that the functions would get called in, that's inside-out: "foo bar baz."
12 Wouldn't it be nice if we could do this instead?
18 Luckily, as you may have guessed with the leading question, you can! Rust provides
19 the ability to use this *method call syntax* via the `impl` keyword.
34 fn area(&self) -> f64 {
35 std::f64::consts::PI * (self.radius * self.radius)
40 let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
41 println!("{}", c.area());
45 This will print `12.566371`.
47 We've made a struct that represents a circle. We then write an `impl` block,
48 and inside it, define a method, `area`. Methods take a special first
49 parameter, of which there are three variants: `self`, `&self`, and `&mut self`.
50 You can think of this first parameter as being the `x` in `x.foo()`. The three
51 variants correspond to the three kinds of thing `x` could be: `self` if it's
52 just a value on the stack, `&self` if it's a reference, and `&mut self` if it's
53 a mutable reference. We should default to using `&self`, as it's the most
54 common, as Rustaceans prefer borrowing over taking ownership, and references
55 over mutable references. Here's an example of all three variants:
66 println!("taking self by reference!");
69 fn mutable_reference(&mut self) {
70 println!("taking self by mutable reference!");
73 fn takes_ownership(self) {
74 println!("taking ownership of self!");
79 Finally, as you may remember, the value of the area of a circle is `π*r²`.
80 Because we took the `&self` parameter to `area`, we can use it just like any
81 other parameter. Because we know it's a `Circle`, we can access the `radius`
82 just like we would with any other struct. An import of π and some
83 multiplications later, and we have our area.
85 ## Chaining method calls
87 So, now we know how to call a method, such as `foo.bar()`. But what about our
88 original example, `foo.bar().baz()`? This is called 'method chaining', and we
89 can do it by returning `self`.
100 fn area(&self) -> f64 {
101 std::f64::consts::PI * (self.radius * self.radius)
104 fn grow(&self, increment: f64) -> Circle {
105 Circle { x: self.x, y: self.y, radius: self.radius + increment }
110 let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
111 println!("{}", c.area());
113 let d = c.grow(2.0).area();
118 Check the return type:
123 fn grow(&self) -> Circle {
127 We just say we're returning a `Circle`. With this method, we can grow a new
128 circle to any arbitrary size.
132 You can also define methods that do not take a `self` parameter. Here's a
133 pattern that's very common in Rust code:
143 fn new(x: f64, y: f64, radius: f64) -> Circle {
153 let c = Circle::new(0.0, 0.0, 2.0);
157 This *static method* builds a new `Circle` for us. Note that static methods
158 are called with the `Struct::method()` syntax, rather than the `ref.method()`
163 Let's say that we want our users to be able to create Circles, but we will
164 allow them to only set the properties they care about. Otherwise, the `x`
165 and `y` attributes will be `0.0`, and the `radius` will be `1.0`. Rust doesn't
166 have method overloading, named arguments, or variable arguments. We employ
167 the builder pattern instead. It looks like this:
178 fn area(&self) -> f64 {
179 std::f64::consts::PI * (self.radius * self.radius)
183 struct CircleBuilder {
189 fn new() -> CircleBuilder {
190 CircleBuilder { coordinate: 0.0, radius: 0.0, }
193 fn coordinate(&mut self, coordinate: f64) -> &mut CircleBuilder {
194 self.coordinate = coordinate;
198 fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
199 self.radius = radius;
203 fn finalize(&self) -> Circle {
204 Circle { x: self.coordinate, y: self.coordinate, radius: self.radius }
209 let c = CircleBuilder::new()
215 println!("area: {}", c.area());
219 What we've done here is make another struct, `CircleBuilder`. We've defined our
220 builder methods on it. We've also defined our `area()` method on `Circle`. We
221 also made one more method on `CircleBuilder`: `finalize()`. This method creates
222 our final `Circle` from the builder. Now, we've used the type system to enforce
223 our concerns: we can use the methods on `CircleBuilder` to constrain making
224 `Circle`s in any way we choose.