Question

Mutating Struct Fields in Rust Methods with &mut self

rustmutable

Question

How can I mutate a field of a Rust struct from one of its methods?

For example, I want up to increase a point's y coordinate:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn up(&self) {
        self.y += 1;
    }
}

fn main() {
    let p = Point { x: 0, y: 0 };
    p.up();
}

However, Rust reports that self.y cannot be assigned through an immutable reference. What changes are required to mutate the field correctly?

Short Answer

You will learn how Rust controls mutation through ownership and borrowing. In particular, you will see why a method that changes a struct needs &mut self, why the caller's variable must be declared with mut, and how to call mutating methods safely.

Concept

Rust distinguishes between reading a value and changing it.

In a method, the self parameter determines how that method accesses its instance:

  • self takes ownership of the value.
  • &self immutably borrows the value. The method may read fields but may not modify them.
  • &mut self mutably borrows the value. The method may read and modify fields.

Your up method changes y, so it requires a mutable borrow:

fn up(&mut self) {
    self.y += 1;
}

The caller must also allow the Point to be mutated by declaring its binding with mut:

let mut p = Point { x: 0, y: 0 };
p.up();

These rules are part of Rust's ownership system. They prevent unexpected changes through read-only references and help Rust guarantee memory safety without a garbage collector.

Mental Model

Think of a struct value as a notebook.

  • &self is borrowing the notebook with read-only permission. You can inspect the coordinates, but you cannot erase or write anything.
  • &mut self is borrowing the notebook with editing permission. You can update a coordinate.
  • let mut p means the notebook is allowed to be edited at all.

Rust requires both permissions: the method must request permission to edit, and the variable owner must grant it.

Take Quiz

Syntax and Examples

A method that changes its struct uses &mut self.

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn up(&mut self) {
        self.y += 1;
    }
}

fn main() {
    let mut p = Point { x: 0, y: 0 };

    p.up();

    println!("({}, {})", p.x, p.y);
}

Output:

(0, 1)

p.up() automatically passes a mutable reference, effectively behaving like this:

Point::up(&mut p);

Rust performs this automatic borrowing only when the variable can legally be mutably borrowed.

Step by Step Execution

Consider this program:

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn up(&mut self) {
        self.y += 1;
    }
}

fn main() {
    let mut p = Point { x: 3, y: 5 };
    p.up();
    println!("{}", p.y);
}

Execution steps:

  1. let mut p creates a mutable binding named p containing Point { x: 3, y: 5 }.
  2. p.up() calls the up method.
  3. Because up expects &mut self, Rust temporarily mutably borrows p as &mut p.
  4. Inside the method, self refers to that mutable borrow.
  5. self.y += 1 changes y from 5 to 6.
  6. The mutable borrow ends when the method returns.
  7. println! reads p.y and prints 6.

While the mutable borrow is active, Rust does not allow conflicting borrows of the same value.

Real World Use Cases

Mutable methods are common when a type represents state that changes over time.

  • Game movement: update a player's position, velocity, health, or score.
  • Counters and metrics: increment a request count or retry count.
  • Shopping carts: add or remove items from a cart struct.
  • Parsers: advance a cursor through input text.
  • Network clients: update connection state, buffers, or cached data.
  • Data processing: append results to a collection held by a struct.

Example: incrementing a retry counter.

struct Job {
    retries: u32,
}

impl Job {
    fn record_retry(&mut self) {
        self.retries += 1;
    }
}
Take Quiz

Real Codebase Usage

In real Rust codebases, methods are usually designed around whether they observe, mutate, or consume a value.

Read-only methods use &self

impl Point {
    fn distance_from_origin(&self) -> f64 {
        let squared = self.x * self.x + self.y * self.y;
        (squared as f64).sqrt()
    }
}

State-changing methods use &mut self

impl Point {
    fn move_by(&mut self, dx: i32, dy: i32) {
        self.x += dx;
        self.y += dy;
    }
}

Methods that consume and replace a value can use self

impl Point {
    fn moved_by(self, dx: i32, dy: i32) -> Self {
        Self {
            x: self.x + dx,
            y: self.y + dy,
        }
    }
}

A common design pattern is to provide both forms when useful:

  • move_by(&mut self, ...) changes the existing value.
  • moved_by(self, ...) -> Self produces an updated value instead.

This makes mutation explicit at the call site and lets callers choose the style that fits their code.

Common Mistakes

Using &self when changing a field

This is invalid because &self is an immutable reference:

impl Point {
    fn up(&self) {
        self.y += 1;
    }
}

Fix it by requesting a mutable reference:

fn up(&mut self) {
    self.y += 1;
}

Forgetting mut on the local variable

This also fails:

let p = Point { x: 0, y: 0 };
p.up();

Even if up takes &mut self, p itself must be mutable:

let mut p = Point { x: 0, y: 0 };
p.up();

Assuming mut applies to only one field

Rust applies mutability to the binding, not to individual ordinary struct fields. With a mutable Point, both x and y may be changed:

let mut p = Point { x: 0, y: 0 };
p.x = 10;
p.y = 20;

If a design needs carefully controlled mutation, keep fields private and expose only appropriate methods.

Holding an immutable borrow while trying to mutate

let mut p = Point { x: 0, y: 0 };
let y_ref = &p.y;
p.up();
println!("{y_ref}");

This is rejected because y_ref still reads from p when p.up() needs exclusive mutable access. Use the read before mutation, or make the immutable borrow end before calling the mutating method.

Using self unnecessarily

This compiles, but moves the point into the method:

fn up(mut self) {
    self.y += 1;
}

The original caller cannot use p afterward unless the method returns the updated value. For in-place updates, &mut self is generally the intended choice.

Comparisons

ReceiverWhat the method receivesCan it mutate fields?Can the caller use the original value afterward?
&selfImmutable referenceNoYes
&mut selfMutable referenceYesYes, after the borrow ends
selfOwnership of the valueYes, if declared mut selfNo, unless the value is returned

Example of each receiver:

impl Point {
    fn y(&self) -> i32 {
        self.y
    }

    fn up(&mut self) {
        self.y += 1;
    }

    fn into_tuple(self) -> (i32, i32) {
        (self.x, self.y)
    }
}

&self is for inspection, &mut self is for in-place updates, and self is for operations that take over or transform the value.

Take Quiz

Cheat Sheet

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    // Read-only access
    fn get_y(&self) -> i32 {
        self.y
    }

    // In-place mutation
    fn up(&mut self) {
        self.y += 1;
    }
}

fn main() {
    let mut p = Point { x: 0, y: 0 };

    p.up();             // Works: p is mutable
    println!("{}", p.get_y());
}

Quick rules:

  • Use &self to read a struct.
  • Use &mut self to change a struct.
  • Declare the caller's binding as let mut value before calling an &mut self method.
  • Rust allows either many immutable borrows or one mutable borrow at a time for the same value.
  • Use self when the method should consume the instance.

FAQ

Why do I need both &mut self and let mut p?

&mut self declares that the method needs write access. let mut p declares that the caller permits p to be mutably borrowed. Rust requires both sides to agree.

Can a method with &self ever change a struct?

Not directly through normal fields. Rust provides specialized interior-mutability types such as Cell and RefCell, but ordinary mutation should use &mut self.

Does &mut self move the struct?

No. It temporarily borrows the struct mutably. The caller still owns the struct after the method finishes.

Can I change just one field without making the entire variable mutable?

Not for normal struct fields. Rust's normal mutability is attached to the variable binding. Use methods and private fields to control which updates are allowed.

Why can only one mutable borrow exist at a time?

Exclusive mutable access prevents data races and invalid references. This rule lets Rust catch many bugs during compilation.

Should I use self or &mut self for an update method?

Use &mut self when you want to update the existing instance. Use self when the method consumes the old value, often returning a new one.

Can a method return &mut Self?

Yes. This is useful for chaining mutating calls:

fn up(&mut self) -> &mut Self {
    self.y += 1;
    self
}
Take Quiz

Mini Project

Description

Build a small position tracker for an object on a two-dimensional grid. The tracker demonstrates read-only methods, mutating methods, and a method that resets state without exposing direct field changes throughout the program.

Goal

Create a mutable Position value, move it in several directions, reset it, and print its coordinates after each operation.

Requirements

Create a Position struct with x and y fields.

Add mutating methods to move up, down, left, and right.

Add a read-only method that returns the coordinates as a tuple.

Add a mutating reset method that returns the position to (0, 0).

Declare the position variable as mutable and print its coordinates after updates.

Take Quiz

Keep learning

Related questions

View more