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. Here's an example of all three variants:
65 println!("taking self by reference!");
68 fn mutable_reference(&mut self) {
69 println!("taking self by mutable reference!");
72 fn takes_ownership(self) {
73 println!("taking ownership of self!");
78 Finally, as you may remember, the value of the area of a circle is `π*r²`.
79 Because we took the `&self` parameter to `area`, we can use it just like any
80 other parameter. Because we know it's a `Circle`, we can access the `radius`
81 just like we would with any other struct. An import of π and some
82 multiplications later, and we have our area.
84 ## Chaining method calls
86 So, now we know how to call a method, such as `foo.bar()`. But what about our
87 original example, `foo.bar().baz()`? This is called 'method chaining', and we
88 can do it by returning `self`.
99 fn area(&self) -> f64 {
100 std::f64::consts::PI * (self.radius * self.radius)
103 fn grow(&self, increment: f64) -> Circle {
104 Circle { x: self.x, y: self.y, radius: self.radius + increment }
109 let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
110 println!("{}", c.area());
112 let d = c.grow(2.0).area();
117 Check the return type:
122 fn grow(&self) -> Circle {
126 We just say we're returning a `Circle`. With this method, we can grow a new
127 circle to any arbitrary size.
131 You can also define methods that do not take a `self` parameter. Here's a
132 pattern that's very common in Rust code:
142 fn new(x: f64, y: f64, radius: f64) -> Circle {
152 let c = Circle::new(0.0, 0.0, 2.0);
156 This *static method* builds a new `Circle` for us. Note that static methods
157 are called with the `Struct::method()` syntax, rather than the `ref.method()`
162 Let's say that we want our users to be able to create Circles, but we will
163 allow them to only set the properties they care about. Otherwise, the `x`
164 and `y` attributes will be `0.0`, and the `radius` will be `1.0`. Rust doesn't
165 have method overloading, named arguments, or variable arguments. We employ
166 the builder pattern instead. It looks like this:
177 fn area(&self) -> f64 {
178 std::f64::consts::PI * (self.radius * self.radius)
182 struct CircleBuilder {
188 fn new() -> CircleBuilder {
189 CircleBuilder { coordinate: 0.0, radius: 0.0, }
192 fn coordinate(&mut self, coordinate: f64) -> &mut CircleBuilder {
193 self.coordinate = coordinate;
197 fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
198 self.radius = radius;
202 fn finalize(&self) -> Circle {
203 Circle { x: self.coordinate, y: self.coordinate, radius: self.radius }
208 let c = CircleBuilder::new()
214 println!("area: {}", c.area());
218 What we've done here is make another struct, `CircleBuilder`. We've defined our
219 builder methods on it. We've also defined our `area()` method on `Circle`. We
220 also made one more method on `CircleBuilder`: `finalize()`. This method creates
221 our final `Circle` from the builder. Now, we've used the type system to enforce
222 our concerns: we can use the methods on `CircleBuilder` to constrain making
223 `Circle`s in any way we choose.