Mehdi Akiki
Rust Failure Atlas / Language and diagnostics

RFA-467 · Case file with fixtures · Case 439 of 694 · Compiler evidence

Lowercase self Needs a Rust Method Receiver

Functions inside impl blocks are methods only when their first parameter is a self receiver. Add &self, &mut self, or self according to ownership, or keep an associated function and pass every required value explicitly.

Reviewed
Rust
Rust 1.98.1, edition 2024
Targets
all Rust targets
Profiles
dev, release, test

Direct answer

What this Rust failure means

Why it happens
Impl placement associates a function with a type, but only a self receiver supplies the concrete instance available through lowercase self.
First discriminating check
Decide whether the operation reads, mutates, consumes, or does not need an instance, then select &self, &mut self, self, or explicit ordinary parameters.

I placed fn current() -> u32 inside impl Counter and read self.0 in its body. Rust emitted E0424 because the function has no lowercase self receiver parameter.

The failing fixture shows that being nested in an impl is not enough to create an instance. An impl may contain both methods and associated functions.

A receiver makes an associated function a method

The associated-item Reference defines a method as an associated function whose first parameter is a self receiver.

Without it, current can be called as Counter::current() and no Counter value is supplied. The body therefore has no local self to inspect.

Rust does not create an implicit receiver based on field access inside the body.

Borrow when reading instance state

The repaired fixture changes the signature to:

fn current(&self) -> u32 {
    self.0
}

The shared receiver is appropriate because reading a Copy integer does not require mutation or ownership. Callers can use method syntax counter.current(), and Rust passes a shared borrow.

The Rust Book method chapter explains receiver shorthand and automatic referencing at call sites.

Choose receiver ownership from the operation

&mut self is for mutation requiring exclusive access. Plain self consumes the instance and is useful for transformations, finalisation, or state transitions that must prevent later use of the old value.

I do not add &self mechanically. I ask what the method promises and what the caller should be allowed to do afterward.

For wrapper and smart-pointer receivers, explicit receiver forms may communicate pinning or ownership more precisely.

Keep it associated when no instance is needed

A constructor such as Counter::new() normally has no receiver because no instance exists yet. A pure helper that depends only on type-level constants can also remain associated.

If such a function needs data, I pass it as ordinary parameters or access an associated constant through Self::CONSTANT. I do not invent an instance receiver solely for call syntax consistency.

This keeps object state dependencies visible.

Lowercase self and capital Self are different

self is a value binding introduced by the receiver. Self is the current type inside a trait or impl. An associated function without a receiver may use Self but not self:

fn zero() -> Self {
    Self(0)
}

Case changes meaning. Compiler messages referring to “expected value” are a clue that lowercase self was used without a value binding.

Trait signatures and impl signatures must agree

If a trait declares fn current(&self), its implementation cannot silently omit the receiver. That may also trigger a trait signature compatibility error.

I update the trait only if the capability itself should change between instance and type-level operation. Otherwise I make the impl mirror the declared receiver exactly.

Method-call consumers rely on this contract.

Closures may capture self inside a method

Once a method has a receiver, a closure in its body may capture self according to use. A nested fn item cannot capture it because nested functions are independent items.

If E0424 appears after extracting a closure into a function, I pass the needed reference explicitly or make the helper another method. This avoids relying on lexical appearance for capture semantics.

My E0424 checklist

Receiver choice also affects concurrency contracts. An &self method can be called concurrently only when the surrounding type and references satisfy the required Sync rules; it does not mean the implementation is automatically thread-safe. Interior mutability may allow state changes through &self, with runtime or atomic coordination. I document that when it is surprising. The receiver states Rust aliasing access, while higher-level consistency still belongs to the type's API and implementation.

  • Does the function parameter list contain a self receiver?
  • Does the operation need an instance at all?
  • Should the receiver be &self, &mut self, or self?
  • Must the caller retain or mutate the value afterward?
  • Am I confusing lowercase self with capital Self?
  • Does a trait declaration require a particular receiver?
  • Did a closure become a nested function and lose capture?
  • Would an explicit ordinary parameter be clearer?

The core principle is that instance access is explicit in Rust. Impl placement establishes association with a type, but only a receiver supplies a value. I choose that receiver from the operation's ownership contract instead of letting body syntax imply one.