8.12 Methods and Associated Functions
Rust allows associating functions directly with struct
s, enum
s, and traits using impl
(implementation) blocks. These associated functions come in two main forms: methods and associated functions (often called “static methods” in other languages).
- Methods: Functions that operate on an instance of a type. Their first parameter is always written as
self
,&self
, or&mut self
. These represent the instance itself, an immutable borrow of the instance, or a mutable borrow, respectively. Methods are called using dot notation (instance.method()
).- Note on
Self
Type: These parameter forms (self
,&self
,&mut self
) are actually shorthand forself: Self
,self: &Self
, andself: &mut Self
. Here,Self
(capital ‘S’) is a special type alias within animpl
block that refers to the type the block is implementing (e.g.,Circle
withinimpl Circle { ... }
). This shows thatself
parameters still follow the standardparameter: Type
syntax.
- Note on
- Associated Functions: Functions associated with a type but not tied to a specific instance. They do not take
self
as the first parameter. They are called using the type name and::
syntax (Type::function()
). They are commonly used for constructors or utility functions related to the type.
8.12.1 Defining and Calling Methods and Associated Functions
struct Circle { radius: f64, } // Implementation block for the Circle struct (Here, Self = Circle) impl Circle { // Associated function: often used as a constructor. // Does not take 'self'. Called using Circle::new(...). pub fn new(radius: f64) -> Self { // 'Self' refers to the type 'Circle' if radius < 0.0 { panic!("Radius cannot be negative"); } Circle { radius } } // Method: takes an immutable reference ('self: &Self'). // Called using my_circle.area(). pub fn area(&self) -> f64 { // Short for 'self: &Self' or 'self: &Circle' std::f64::consts::PI * self.radius * self.radius } // Method: takes a mutable reference ('self: &mut Self'). // Called using my_circle.scale(...). pub fn scale(&mut self, factor: f64) { // Short for 'self: &mut Self' or 'self: &mut Circle' if factor < 0.0 { panic!("Scale factor cannot be negative"); } self.radius *= factor; } // Method: takes ownership ('self: Self'). // Called using my_circle.consume(). The instance cannot be used afterwards. pub fn consume(self) { // Short for 'self: Self' or 'self: Circle' println!("Consuming circle with radius {}", self.radius); // 'self' (the circle instance) is dropped here. } } fn main() { // Call associated function (constructor) let mut my_circle = Circle::new(5.0); // Call methods using dot notation println!("Initial Area: {}", my_circle.area()); my_circle.scale(2.0); // Calls the mutable method println!("Scaled Radius: {}", my_circle.radius); println!("Scaled Area: {}", my_circle.area()); // Call method that consumes the instance // my_circle.consume(); // println!("Area after consume: {}", my_circle.area()); // Error: use of moved value 'my_circle' // Alternative way to call methods (less common): // Explicitly pass the instance reference. let radius = 10.0; let another_circle = Circle::new(radius); let area = Circle::area(&another_circle); // Equivalent to another_circle.area() println!("Area of another circle: {}", area); }
As noted in Section 6.3.1, Rust performs automatic referencing and dereferencing for method calls. When using the dot operator (object.method()
), the compiler automatically inserts the appropriate &
, &mut
, or *
to match the method’s self
, &self
, or &mut self
receiver as required.