ProgrammingRust Backend Developer

How are methods and associated functions structured in Rust? How do they differ from each other, how to declare and call them, and when should each variant be used?

Pass interviews with Hintsage AI assistant

Answer.

Background

Rust borrows the concept of methods from object-oriented languages, but implements them differently: instead of the familiar this or self, methods explicitly take a self parameter. Associated functions appeared as an alternative to static methods in other programming languages—they are tied to a type but not to a specific value.

The Problem

People often confuse methods, associated functions, and free functions. It is not clear when to use a method with self and when to use an associated function without self. There are questions of visibility, auto-dereferencing, and ownership transfer.

The Solution

Methods in Rust are declared with the first parameter being self/ &self/ &mut self inside an impl block (usually for struct or enum). They are called on an instance: object.method(). Associated functions (e.g., new, from) are also declared inside impl but without the first parameter self and are invoked using the double colon: Type::function().

Example code:

struct Point { x: f64, y: f64, } impl Point { // Associated function (constructor) fn new(x: f64, y: f64) -> Self { Self { x, y } } // Method: requires self fn distance_from_origin(&self) -> f64 { (self.x.powi(2) + self.y.powi(2)).sqrt() } } let p = Point::new(3.0, 4.0); printf!("{}", p.distance_from_origin()); // 5.0

Key features:

  • Methods take the self parameter in various ownership modes
  • Associated functions do not take self, commonly used for initialization or utilities
  • Only methods can be called on an instance using dot notation, whereas associated functions can only be called using ::

Tricky Questions.

Can associated functions be called through an instance (via dot notation)?

This is possible (e.g., p.new(1.0, 2.0)), but highly discouraged: it is misleading because an associated function does not have access to the current object, and the instance is passed while being ignored. It is better to use the syntax Type::func().

Example code:

let p = Point::new(1.0, 2.0); let q = p.new(0.0, 0.0); // Works, but not a best practice!

Can methods be asynchronous?

Yes. Methods can be declared with the async keyword just like free functions:

impl Foo { async fn do_async(&self) { // ... } }

Can both methods and associated functions be declared in the same impl block?

Yes—any combinations are allowed. It is also possible to declare multiple impl blocks for a single type.

Common Errors and Anti-patterns

  • Confusing methods and associated functions
  • Leaving associated functions without type affiliation (not declaring in impl)
  • Calling associated functions through an instance (via dot notation)

Real-life Example

Negative Case

A beginner declared the new function outside of impl and tried to use it as a constructor, then accidentally called it through an instance: p.new(1.0, 2.0).

Pros:

Works quickly (the compiler allows it).

Cons:

Code is unreadable, hard to maintain, difficult to use methods with correct self ownership.

Positive Case

All methods and associated functions are strictly declared inside impl, using correct calling syntaxes (Type::new() for constructors, obj.method() for actions).

Pros:

High readability, adherence to best practices.

Cons:

Requires knowledge of Rust idioms and attention to syntax.