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`.
46 We’ve made a `struct` that represents a circle. We then write an `impl` block,
47 and inside it, define a method, `area`.
49 Methods take a special first parameter, of which there are three variants:
50 `self`, `&self`, and `&mut self`. You can think of this first parameter as
51 being the `foo` in `foo.bar()`. The three variants correspond to the three
52 kinds of things `foo` could be: `self` if it’s just a value on the stack,
53 `&self` if it’s a reference, and `&mut self` if it’s a mutable reference.
54 Because we took the `&self` parameter to `area`, we can use it just like any
55 other parameter. Because we know it’s a `Circle`, we can access the `radius`
56 just like we would with any other `struct`.
58 We should default to using `&self`, as you should prefer borrowing over taking
59 ownership, as well as taking immutable references over mutable ones. Here’s an
60 example of all three variants:
71 println!("taking self by reference!");
74 fn mutable_reference(&mut self) {
75 println!("taking self by mutable reference!");
78 fn takes_ownership(self) {
79 println!("taking ownership of self!");
84 You can use as many `impl` blocks as you’d like. The previous example could
85 have also been written like this:
96 println!("taking self by reference!");
101 fn mutable_reference(&mut self) {
102 println!("taking self by mutable reference!");
107 fn takes_ownership(self) {
108 println!("taking ownership of self!");
113 # Chaining method calls
115 So, now we know how to call a method, such as `foo.bar()`. But what about our
116 original example, `foo.bar().baz()`? This is called ‘method chaining’. Let’s
127 fn area(&self) -> f64 {
128 std::f64::consts::PI * (self.radius * self.radius)
131 fn grow(&self, increment: f64) -> Circle {
132 Circle { x: self.x, y: self.y, radius: self.radius + increment }
137 let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
138 println!("{}", c.area());
140 let d = c.grow(2.0).area();
145 Check the return type:
150 fn grow(&self, increment: f64) -> Circle {
154 We just say we’re returning a `Circle`. With this method, we can grow a new
155 `Circle` to any arbitrary size.
157 # Associated functions
159 You can also define associated functions that do not take a `self` parameter.
160 Here’s a pattern that’s very common in Rust code:
170 fn new(x: f64, y: f64, radius: f64) -> Circle {
180 let c = Circle::new(0.0, 0.0, 2.0);
184 This ‘associated function’ builds a new `Circle` for us. Note that associated
185 functions are called with the `Struct::function()` syntax, rather than the
186 `ref.method()` syntax. Some other languages call associated functions ‘static
191 Let’s say that we want our users to be able to create `Circle`s, but we will
192 allow them to only set the properties they care about. Otherwise, the `x`
193 and `y` attributes will be `0.0`, and the `radius` will be `1.0`. Rust doesn’t
194 have method overloading, named arguments, or variable arguments. We employ
195 the builder pattern instead. It looks like this:
205 fn area(&self) -> f64 {
206 std::f64::consts::PI * (self.radius * self.radius)
210 struct CircleBuilder {
217 fn new() -> CircleBuilder {
218 CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, }
221 fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
226 fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
231 fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
232 self.radius = radius;
236 fn finalize(&self) -> Circle {
237 Circle { x: self.x, y: self.y, radius: self.radius }
242 let c = CircleBuilder::new()
248 println!("area: {}", c.area());
249 println!("x: {}", c.x);
250 println!("y: {}", c.y);
254 What we’ve done here is make another `struct`, `CircleBuilder`. We’ve defined our
255 builder methods on it. We’ve also defined our `area()` method on `Circle`. We
256 also made one more method on `CircleBuilder`: `finalize()`. This method creates
257 our final `Circle` from the builder. Now, we’ve used the type system to enforce
258 our concerns: we can use the methods on `CircleBuilder` to constrain making
259 `Circle`s in any way we choose.