In Rust, methods are functions associated with an instance of a particular data type (like a struct or an enum), rather than standalone functions. They are a fundamental concept for encapsulating behavior related to a specific type.
Defining Methods
Methods are defined within an `impl` (implementation) block for a given type. This block houses all methods and associated functions for that type.
```rust
struct MyStruct {
value: i32,
}
impl MyStruct {
// Methods and associated functions go here
}
```
The `self` Parameter
The distinguishing feature of a method (compared to an associated function) is that its first parameter is always `self`, which refers to the instance of the type the method is being called on. The way `self` is passed determines how the method interacts with the instance:
1. `&self` (Immutable Reference):
* This is the most common way to define methods. It allows the method to read data from the instance without taking ownership or mutating its state.
* The instance remains fully usable after the method call.
* Example: `fn area(&self) -> u32`
2. `&mut self` (Mutable Reference):
* This allows the method to modify the instance's internal data.
* The instance is borrowed mutably, meaning no other immutable or mutable borrows can exist simultaneously while the method is active.
* The instance remains usable after the method call, but its state might have changed.
* Example: `fn scale(&mut self, factor: u32)`
3. `self` (Ownership Transfer):
* This makes the method take ownership of the instance. The instance is moved into the method.
* After the method call, the original instance can no longer be used (it's been 'consumed').
* This is typically used for methods that transform `self` into something else (e.g., `String::into_bytes()`) or perform a final operation that requires ownership.
* Example: `fn into_square(self) -> Self`
Associated Functions vs. Methods
* Associated Functions: These are functions defined within an `impl` block that *do not* take `self` as their first parameter. They are typically used as constructors (e.g., `String::new()`, `Rectangle::new()`) or utility functions that operate on the type itself rather than an instance. They are called using the double colon `::` syntax: `TypeName::function_name()`.
* Methods: These are functions defined within an `impl` block that *do* take `self` (or `&self`, `&mut self`) as their first parameter. They are called using the dot operator `.` on an instance: `instance.method_name()`.
The Dot Operator (`.`)
Rust's dot operator for calling methods is smart. It automatically handles referencing and dereferencing. If you have an instance `my_struct`, and a method `my_method` that expects `&self`, you can simply call `my_struct.my_method()`. Rust will automatically borrow `my_struct` as `&my_struct` for you. This applies to `&mut self` as well, making method calls ergonomic.
Method Chaining
Methods that return a reference to `self` (e.g., `&self` or `&mut self`) can often be chained, allowing for more concise and readable code. For example, if `method1` returns `&mut self` and `method2` is also on `&mut self`, you can write `instance.method1().method2();`.
Example Code
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// Associated Function (Constructor) - does not take `self`
// Called like: `Rectangle::new(w, h)`
fn new(width: u32, height: u32) -> Self {
Rectangle { width, height }
}
// Method with `&self` (immutable reference)
// Allows reading `self`'s data without taking ownership or mutating it.
// Called like: `my_rect.area()`
fn area(&self) -> u32 {
self.width * self.height
}
// Method with `&self` (immutable reference)
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
// Method with `&mut self` (mutable reference)
// Allows modifying `self`'s data. Returns `&mut Self` for chaining.
// Called like: `my_rect.scale(factor)`
fn scale(&mut self, factor: u32) -> &mut Self {
self.width *= factor;
self.height *= factor;
self // Return the mutable reference for chaining
}
// Method with `self` (takes ownership)
// Consumes the instance and returns a new one (or transforms it).
// The original instance can no longer be used after this call.
// Called like: `my_rect.into_square()`
fn into_square(self) -> Self {
let side = self.width.min(self.height);
println!(" -> Consumed original Rectangle to create a square with side: {}", side);
Rectangle::new(side, side)
}
// Another method returning `&Self` for possible chaining or inspection
fn print_dimensions(&self) -> &Self {
println!("Dimensions: {}x{}", self.width, self.height);
self // Return self for chaining if needed
}
}
fn main() {
println!("--- Demonstrating Method Syntax in Rust ---");
// 1. Associated Function Call (like a static method or constructor)
let mut rect1 = Rectangle::new(30, 50);
println!("Created rect1 using Rectangle::new(30, 50).");
// 2. Method Call with &self (immutable borrow)
println!("Rect1's initial area: {}", rect1.area()); // rect1 is borrowed immutably
let rect2 = Rectangle::new(10, 40);
println!("Rect1 can hold rect2: {}", rect1.can_hold(&rect2)); // rect1 is borrowed immutably
// 3. Method Call with &mut self (mutable borrow)
println!("\n--- Scaling rect1 ---");
println!("Rect1 before scaling:");
rect1.print_dimensions(); // Immutable borrow to print
rect1.scale(2); // rect1 is borrowed mutably here
println!("Rect1 after scaling by 2:");
rect1.print_dimensions(); // Immutable borrow to print
println!("Rect1's new area: {}", rect1.area());
// 4. Method Call with `self` (ownership transfer)
println!("\n--- Transforming rect1 into a square ---");
println!("Original rect1 dimensions before into_square:");
// We cannot print rect1 directly after `into_square` because it's moved.
// Let's create a temporary one to show its state just before consumption.
let temp_rect = Rectangle::new(rect1.width, rect1.height);
temp_rect.print_dimensions();
let square_rect = rect1.into_square(); // rect1 is consumed here!
// If you uncomment the line below, it will cause a compile-time error:
// println!("Trying to use rect1 after into_square: {:?}", rect1); // ERROR: use of moved value!
println!("Created square_rect after consuming original rect1:");
square_rect.print_dimensions();
println!("Square_rect's area: {}", square_rect.area());
// 5. Method Chaining
println!("\n--- Demonstrating Method Chaining (with `&mut Self` return) ---");
let mut chained_rect_2 = Rectangle::new(7, 12);
println!("Initial chained_rect_2:");
chained_rect_2.print_dimensions();
println!("Chaining scale and print_dimensions:");
chained_rect_2
.scale(2) // Returns &mut Self
.print_dimensions() // Returns &Self
.scale(3) // Returns &mut Self (requires mut, which is fine here)
.print_dimensions(); // Returns &Self
println!("Final chained_rect_2 area: {}", chained_rect_2.area());
}








Method Syntax