Why is `mut` linted here?

In the following code (which is contrived, but reproduces a real-world reasonable scenario, I get the lint warning: variable does not need to be mutable for the bar function. And sure enough, I can remove mut and the code still works. And yet the bar function mutates self through the set_bar function.

So what's up?

struct Foo {
	bar: i32
}

impl Foo {
	fn set_bar (mut self, bar: i32) -> Self {
		self.bar = bar;
		self
	}

	fn bar(mut self, bar: i32) -> Self {
		self.set_bar(bar)
	}
}

Here's my full running example:

#[derive(Debug)]
struct Foo {
    bar: i32
}

impl Foo {
    fn set_bar(mut self, bar: i32) -> Self {
        self.bar = bar;
        self
    }

    fn bar(self, bar: i32) -> Self {
        self.set_bar(bar)
    }
}

fn main() {
    let foo = Foo { bar: 13 };

    let foo2 = foo.set_bar(42);
    println!("{:?}", foo2);

    let foo3 = foo2.bar(54);
    println!("{:?}", foo3);
}

The reason that this isn't needed is that you're taking ownership. You're re-assigning self, which is what mut means here. You can see this reflected in the need for foo2 and foo3 in main(), we've moved foo and so cannot use it.

I think this is the code you wanted to write:

#[derive(Debug)]
struct Foo {
    bar: i32
}

impl Foo {
    fn set_bar(&mut self, bar: i32) {
        self.bar = bar;
    }

    fn bar(&mut self, bar: i32) {
        self.set_bar(bar)
    }
}

fn main() {
    let mut foo = Foo { bar: 13 };

    foo.set_bar(42);
    println!("{:?}", foo);

    foo.bar(54);
    println!("{:?}", foo);
}

By taking &mut self, you're actually doing mutation of what the binding is pointing to, rather than changing the binding.

1 Like

The difference hit me when I tried the following:

let f = Foo{bar:5};
let _ = f.bar(20);

And hit a moved value error. Which means that my intuition for what Rust is doing is still very immature.

The reason to return `self`` is to allow fluid method chaining. I think I now understand that that's underlying implemented as a sequence of moves of ownership?

Still so much to learn.

Exactly! By constantly returning self you give up ownership of self to the caller again, so it can go on calling new methods. It's a common approach. Just don't forget to reassign the result of a call in your call site to avoid losing ownership. The drawback is you make a copy of self with each of such call and return, so if your type is large enough, you better pass a reference (&mut self if you need to mutate your state).