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.
33 fn area(&self) -> f64 {
34 std::f64::consts::PI * (self.radius * self.radius)
39 let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
40 println!("{}", c.area());
44 This will print `12.566371`.
48 We’ve made a struct that represents a circle. We then write an `impl` block,
49 and inside it, define a method, `area`.
51 Methods take a special first parameter, of which there are three variants:
52 `self`, `&self`, and `&mut self`. You can think of this first parameter as
53 being the `foo` in `foo.bar()`. The three variants correspond to the three
54 kinds of things `foo` could be: `self` if it’s just a value on the stack,
55 `&self` if it’s a reference, and `&mut self` if it’s a mutable reference.
56 Because we took the `&self` parameter to `area`, we can use it just like any
57 other parameter. Because we know it’s a `Circle`, we can access the `radius`
58 just like we would with any other struct.
60 We should default to using `&self`, as you should prefer borrowing over taking
61 ownership, as well as taking immutable references over mutable ones. Here’s an
62 example of all three variants:
73 println!("taking self by reference!");
76 fn mutable_reference(&mut self) {
77 println!("taking self by mutable reference!");
80 fn takes_ownership(self) {
81 println!("taking ownership of self!");
86 # Chaining method calls
88 So, now we know how to call a method, such as `foo.bar()`. But what about our
89 original example, `foo.bar().baz()`? This is called ‘method chaining’, and we
90 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.
130 # Associated functions
132 You can also define associated functions that do not take a `self` parameter.
133 Here’s a 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 ‘associated function’ builds a new `Circle` for us. Note that associated
158 functions are called with the `Struct::function()` syntax, rather than the
159 `ref.method()` syntax. Some other languages call associated functions ‘static
164 Let’s say that we want our users to be able to create Circles, but we will
165 allow them to only set the properties they care about. Otherwise, the `x`
166 and `y` attributes will be `0.0`, and the `radius` will be `1.0`. Rust doesn’t
167 have method overloading, named arguments, or variable arguments. We employ
168 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 {
190 fn new() -> CircleBuilder {
191 CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, }
194 fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
199 fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
204 fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
205 self.radius = radius;
209 fn finalize(&self) -> Circle {
210 Circle { x: self.x, y: self.y, radius: self.radius }
215 let c = CircleBuilder::new()
221 println!("area: {}", c.area());
222 println!("x: {}", c.x);
223 println!("y: {}", c.y);
227 What we’ve done here is make another struct, `CircleBuilder`. We’ve defined our
228 builder methods on it. We’ve also defined our `area()` method on `Circle`. We
229 also made one more method on `CircleBuilder`: `finalize()`. This method creates
230 our final `Circle` from the builder. Now, we’ve used the type system to enforce
231 our concerns: we can use the methods on `CircleBuilder` to constrain making
232 `Circle`s in any way we choose.