SoFunction
Updated on 2025-04-09

Interpretation of methods and associative functions in Rust

1. What are methods?

In Rust, methods and functions are defined very similarly:

  • Use allfnLet's declare.
  • All can have parameters and return values.
  • All contain a piece of code logic executed when called.

The difference is:Methods must be defined in a specific type (e.g.structenumor in the context of a trait object). And the first parameter of the method must be written asself(can beself&selfor&mut self), used to represent a specific instance of calling the method.

Let's take a look at a simple example. Suppose we have oneRectangleStructure:

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

If you want toRectangleThe instance adds a function to calculate area, we canimplDefine a method for it in the (implementation) blockarea

impl Rectangle {
    fn area(&self) -> u32 {
         * 
    }
}
  • hereimpl Rectangle { ... }Indicates that all functions in this block areRectangleType association.
  • fn area(&self) -> u32Note: This is a method, the first parameter is&self,expressIn the form of immutable referencesAccess the currently called methodRectangleExample.
  • andThat is, the field that represents the instance. useselfAccess fields are very intuitive.

existmainIn the function, after we create a rectangle instance, we can use method syntax to get the area:

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    
    println!("rect1 The area is:{}", ());
}

After running, the output will be:

The area of ​​rect1 is: 1500

2. Why use &self instead of &Rectangle?

In ourareaWhen refactoring from a normal function into a method, you will notice that the function signature is from

fn area(rectangle: &Rectangle) -> u32 { ... }

Become

fn area(&self) -> u32 { ... }

This is becauseimpl RectangleIn this context, Rust gives a more readable way: let the first parameter automatically becomeself,andSelfIt is the type alias corresponding to the current implementation block.

If you needReviseThe field of the instance, you can write the first parameter as&mut self; if neededObtain ownershipAnd maybe "convert" it into something else inside the method, then useself. But this kind of transfer of ownership to the method itself is rare.

In most cases, we just want to read the structure data without changing it, then use&selfMost commonly, it also allows the caller to continue using this instance.

3. Methods of the same name field and the same name

If you areRectangleThere is also a field calledwidth, and also want to define a method calledwidth, this is legal. for example:

impl Rectangle {
    fn width(&self) -> bool {
         > 0
    }
}

When called:

  • (without brackets) Accessed is a fieldwidthvalue of .
  • ()(with brackets) calls a method with the same name, returning a boolean value.

In many languages, if you just want to return field values ​​simply, this method will be called "getter".

Rust does not automatically generate getters for you, but you can define them yourself.

In this way, you can just set the field to private, but expose the read-only method to the outside world, allowing it to be accessed safely from the outside.

4. Borrowing and dereference: Why don't you need to write & or * when calling a method?

In C/C++, if you want to call member functions through pointers, you need to write->. Or, if you have a pointer on hand, explicitly(*object).method()wait.

There is no need to be such trouble in Rust becauseAutomatic reference and dereferenceLet you write directly()

In fact, these calls are the same:

(&p2);
(&p1).distance(&p2);

Rust will be signed according to the method (the first parameter is&self&mut selfstillself) to automatically infer whether you need to add it for you&&mutor*. This greatly simplifies the syntax when calling methods.

5. Methods can have multiple parameters

There is no difference between methods and functions in terms of parameters exceptThe first parameter isselfYou can add other parameters freely.

For example,RectangleDefine another methodcan_hold, used to check whether the "current rectangle" can fully accommodate another rectangle:

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
         >  &&  > 
    }
}

Then use it like this:

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };
    let rect2 = Rectangle { width: 10, height: 40 };
    let rect3 = Rectangle { width: 60, height: 45 };

    println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2)); // true
    println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3)); // false
}

6. Associated Functions

IfimplFunctions defined in blocksNo selfParameter, then it is not a method, butAssociated function

Associative functions are often used to provide functions similar to "constructors".

For example, if you want to quickly construct a "square":

impl Rectangle {
    // Associative function    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}

When calling, use::Syntax to call the associative function:

fn main() {
    let sq = Rectangle::square(3);
    println!("square sq: {:#?}", sq);
}

The print result is:

Square sq: Rectangle {
    width: 3,
    height: 3
}

In the standard library, we often see such asString::from("Hello"). It does not require an existing oneStringInstance can be called directly to create a new string.

7. Multiple impl blocks

You can write multiple for the same typeimplBlocks, such as:

impl Rectangle {
    fn area(&self) -> u32 {
         * 
    }
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
         >  &&  > 
    }
}

It's the same as writing them in the same wayimplThere is no essential difference in it. The reason Rust allows you to write separately is to make it more flexible in organizing code in some cases (such as generics, trait implementations, etc.).

8. Summary

  • method: Must be defined in a certain type (e.g.struct)ofimplIn the block, the first parameter isself(mutable or immutable). Methods are often used to describe certain behaviors of an instance of that type, to read or write its internal data.
  • Associative Functions:existimplDefined in blocks but not includedselfFunction of parameters. Commonly used to construct new instances or provide some functionality that is not related to the instance.
  • Rust ownsAutomatic reference and dereferenceFeatures that allow us to use it concisely()To call the method.
  • MultipleimplBlocks can coexist, providing great flexibility to the organization of code.

By defining methods for custom types, we can not only make the code more readable and put related behaviors into the sameimplIn the block, you can also make full use of ownership, borrowing and other features to ensure memory security and concurrency security.

Hope this article helps you figure out how to write and when to use it in Rust&self&mut selfself, and how to make the code more concise and elegant with the help of associative functions.

If you are still interested in enums or traits in Rust, you might as well continue reading the following chapters, includingstructLikewise, it is also an important tool for building complex logic.

Of course, the above is personal experience. I hope you can give you a reference and I hope you can support me more.