Const binding to mutable object

Hi There,

The more I try the more I'm lost... I'm trying a few thing to learn Rust and the moment I think I unsertand something and move and make some change my code does not compile anymore :frowning:

In 5.11 Mutability of the book it is said:

"When a binding is mutable, it means
you’re allowed to change what the binding points to. So in the above example,
it’s not so much that the value at x is changing, but that the binding
changed from one i32 to another."

The last part of the sentence "it's not so much..." is really confusing to me.
I think I'm trying to figure out how to express a constant reference (more in the C++ sense of the term) to a mutable object.

Say I have struct Foo { a: i32, }. In

let f = Foo { a: 1 }; 

f is more like a constant reference to a constant object as I can't rebind f to another Foo nor modify f.a`. And in

let mut f = Foo { a: 1 };

f would be more like a mutable reference to a mutable object as I can do:

f.a = 2;    
f = Foo { a: 10 };

Can someone please tell me where to start to achieve something similar to a const ref to a mutable object ?

Thanks

My understanding is that it would be:

let mut x = 5;
let r = &x;

Or to use your example:

let mut f = Foo { a: 1 };
let r = &f;

@steveklabnik Hmm, I'm also confused by this

It would make me think that after x = 6 the 5 still exists somewhere.

let mut x = 5;
x = 6;

becomes

  %x = alloca i32
  store i32 5, i32* %x, align 4
  store i32 6, i32* %x, align 4

(looks like changing the value to me)

In,

let mut f = Foo { a: 1 };
let r = &f; 

You can't modify the struct.

borrow.rs:8:2: 8:9 error: cannot assign to immutable field `r.a`
borrow.rs:8     r.a = 2;
                ^~~~~~~
error: aborting due to previous error

You can of course do:

let mut f = Foo { a: 1 };
let r = &mut f;

that would work.

My point is, the mut seems to apply to both the binding, ie the ability to rebind x to something else, and the Foo object, the ability to modify it... I don't know this is nothing but it's just one thing that confuses me among many others :smile:

[quote="applequist, post:1, topic:2715"]
a const ref to a mutable object
[/quote]Based on your examples, this should be it and seems covered in that section:

let mut a = 1;
let mut b = 2;
let x = &mut a;
*x = 0;
x = &mut b; // error: x is immutable
2 Likes

Hm, that's not what I meant by this. But regardless, LLVM may optimize all sorts of things, so when speaking about language semantics sometimes they won't be the same as the generated assembly. Such is the job of an optimizing compiler!

That's LLVM IR not assembly. Did LLVM have a chance to optimize any of it?

Thanks for your help ! Maybe a more practical question would better help me :smile:

The thing is I just want to pass some kind of state object to several other objects that could mutate the state object in turn, no concurrency, thread and whatnot's, just a purely sequential program, Say:

struct State {
        a: i32,
}
struct Part<'a> {
        state: &'a State,
}
impl<'a> Part<'a> {
        fn do_stuff(&self) {
                self.state.a += 1;
        }
}
fn main() {
        let mut state = State { a: 1 };
        let part1 = Part { state: &state };
        let part2 = Part { state: &state };
        part1.do_stuff();
        part2.do_stuff();
}

This does not compile with the following error:

borrow.rs:9:3: 9:20 error: cannot assign to immutable field `self.state.a`
borrow.rs:9         self.state.a += 1;
                    ^~~~~~~~~~~~~~~~~
error: aborting due to previous error

Whoops, you are right! I'm not sure how much optimizing we're doing before we hit LLVM. My point, broadly speaking, was just that when discussing semantics, you have to figure out which level you're talking at: language semantics, what the optimizer produces, etc. When discussing language semantics, looking at LLVM or assembly output isn't necessarily instructive.

Regardless, all of that is beyond the point: if the text is confusing, it should be fixed.

Here's a version that works: Rust Playground

You need to change all of the &Ts to &mut Ts, and because there can only be one &mut to something at a time, you can't share state between the two Parts.

several other objects that could mutate the state object in turn, no
concurrency, thread and whatnot's, just a purely sequential program,

There are still problems with two mutable pointers to one thing, even in a sequential program, like iterator invalidation.

If they need to be the same State, you can use various standard library tools like RefCell too...

Thanks a lot for your time. But that's not really what I want :blush:.
I was just looking as RefCell but it's defeat the compile-time safety built in Rust...

1 Like

Keep in mind that RefCell doesn't allow defeating memory safety. You can also look at Cell which has somewhat different properties (in particular, it can't panic).

However, if you want to ensure at compile time that only one method call has access to the state at a time, you're going to have to pass it directly as a parameter.

1 Like