Rust LogoMethod Syntax

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());
}