Mutable Immutable, beginner misunderstanding


#1

I am beginner in rust and i have some misunderstand and wondering, about mut and immutable so i decided to shared them to explain as a rust beginner what the questions i have, what feel not intuitive for me and try to understand the logic behind. I welcome any explanations and discussions on what you feeling about

In brief my questions are

  1. if mutable is a status of variable in a specific scope, so the decided on this status will be only when you receiving variable into a scope or change it in a middle scope not in return value,
    if not what the exact definition of the mutable in rust ? and why you need to decided in a function if the variable return as mut /immut, who that use in the function get the decision according its scoop definition

  2. Rust prevent reference of more then one mutable to one variable in a scope. what is the reason ? if the reason (as i assumed) is you can to change the reference of anther variable, what about relative reference to the struct root in rust to solve that problem there is something like that in rust ? if i understand right the problem in these scenario is safety not a wrong pattern (some time for clear code reasons you holds some variables of the same structs) there is a way to solve only the safety issue

(Sorry about my English)
thanks in advance

Questions to steps , each step is a block of code with notes as a comment

Step 1

fn main() {
    // Create mutable person in this scope,
    // assumptions:
    // 1. By write let mut before the variable  name 'p' it become mutable.
    // 2. You can change the p and its fields so mutable related to the entire struct.
    // 3. mut is status of a variable in specific scope.
    let mut p = Person {
        name: "Moshe".into(),
        age: 29,
    };

    // I can change the name, p is mutable
    p.name = "New Name".into();

    // I revive name and age as mut because i want to change them
    // (get_name for me it's a black box that return some ref to 'name', also i now that it not change my struct because it get self as immutable)
    // the assumption 1 as before (by write let mut, name/age become mutable)
    let mut name = p.get_name();
    let mut age = p.get_age();

    // lets change the name
    // Oops i got a error: cannot assign to immutable borrowed content `*name / age`
    // but i receive them as mutable as i receive p.
    *name = "Bob".into();
    *age = 35;

    println!("Hi i am {} {} yours old", name, age);
    println!("Hi i am {} {} yours old", p.name, p.age);
}

#[derive(Debug)]
struct Person {
    name: String,
    age: u32,
}

impl Person {
    fn get_name(&self) -> &String {
        // self is immutable because there is no change in the self > make sense
        &self.name
    }

    fn get_age(&self) -> &u32 {
        // the same as get_name
        &self.age
    }
}

Step 2
So i fixed the previous error by creating to anther functions that get self and return its field as mutable

fn main() {
    // Create mutable person in this scope
    let mut p = Person {
        name: "Moshe".into(),
        age: 29,
    };

    // the solution
    // so the assumption as before (by write let mut, name/age become mutable) not right when you turn to method,
    // so the mut is not only status of a variable in specific scope it more like a type / reference
    let name = p.get_name_mut();
    // lets change the name. now it works
    *name = "Bob".into();
    // error cannot borrow `p` as mutable more than once at a time
    // that means when holding mutable referance to a variable (struct Person in this case) you can't  hold more then once.
    // It can try to infer why ( not changing the ref of the name by p.name )
    let age = p.get_age_mut();

    // I got a error:
    // cannot assign to immutable borrowed content `*name / age`
    // *age = 35;

    println!("Hi i am {} {} yours old", name, age);
}

#[derive(Debug)]
struct Person {
    name: String,
    age: u32,
}

impl Person {

    fn get_name_mut(&mut self) -> &mut String {
        // I don't want self as mutable becuse no change in self ocured here, 
        // but rust force me if i want to return mutable (but according to assumption 3 (mut is status of a variable in specific scope), 
        // why i need to decided how name will returned (mut /immut) this is not a part of the function business)
        &mut self.name
    }

    fn get_age_mut(&mut self) -> &mut u32 {
        // the same as get_name_mut
        &mut self.age
    }
}

Step 3
To solve the previous error i wrapped the name and age to a internal scope

fn main() {
    // Create mutable person in this scope
    let mut p = Person {
        name: "Moshe".into(),
        age: 29,
    };

    let name;
    let age;

    // the solution to the previous errors
    // wrap the name and age to a internal scope
    {
        let _name = p.get_name_mut();
        // lets change the name. now it works
        *_name = "Bob".into();
        name = &*_name;

        // i can't try to change name because now it immutable
        // *name = "Not will work";
    }

    {
        // now we got a error here: cannot borrow `p` as mutable more than once at a time
        // OK one is mutable p, but where is the scones? (name is immutable) 
        // probably the reason is because there is mutable and immutableto the same struct 
        // but the question what the right pattern for these scenarios ?  
        let _age = p.get_age_mut();
        *_age = 35;
        age = &*_age;
    }

    println!("Hi i am {} {} yours old", name, age);
}

#[derive(Debug)]
struct Person {
    name: String,
    age: u32,
}

impl Person {

    fn get_name_mut(&mut self) -> &mut String {
        // I don't want self as mutable because no change in self occurred here, 
        // but rust force me if i want to return mutable (but according to assumption 3, 
        // why i need to decided how name will returned (mut /immut) this is not a part of the function business)
        &mut self.name
    }

    fn get_age_mut(&mut self) -> &mut u32 {
        // the same as get_name_mut
        &mut self.age
    }
}

#2

Don’t have a time for a thorough reply here, so I’ll just drop a tiny bit of info :slight_smile:

Mutability in Rust can be confusing because &mut dost not actually mean mutable, and & does not mean immutable. Rather, these are statements about aliasing: &mut means unique access, and & means potentially shared access. mut on the local variables is sort of a lint: Rust safety rules won’t work without &mut references, but it is in theory possible to remove mut on bindings form the language.

See this (oldish) blog-posts which explains this mutable-vs-unique terminology: http://smallcultfollowing.com/babysteps/blog/2014/05/13/focusing-on-ownership/


#3
  1. I probably would split it into two parts. An imitable variable can only be instantiated only once. The fields of a variable can either be internally-mutable or not, with only immutable access you can only read the fields that aren’t internally-immutable.
    doc on ownership might give more info.

  2. It allows the compiler to optimise code without having to perform (if wanting the optimization) complex https://en.wikipedia.org/wiki/Alias_analysis

The typical approach to solving your last code might be to take an owned structure by cloning.
An alternative is a function giving multiple mutable references;

fn get_fields_mut(&mut self) -> (&mut String, &mut u32) {
    (&mut self.name, &mut self.age)
}
let (name, age) = p.get_fields_mut();
*name = "Bob".into();
*age = 35;

#4

@matklad Thanks about the distinction between mut and &mut, nice article. I will happy to get more info about how to handle with only &mut in a scope when you need to change fields without losing your access to the struct root

@jonh Thanks on your response

I know that there is anther way in this specific case:

 let Person { name, age } = &mut p;
 *name = "Bob".into();
 *age = 35;

But as a pattern this feel me not modular at all, you force to write functions on your struct according to specific cases because you have a problem to turn to tow functions that return &mut on the same scope. I search for a way to solve that issue change the fields where you need and not losing the struct root.


#5

Yes, getters/setters are problematic in Rust and should be avoided. Rust isn’t fully an Object-Oriented language, so typical OOP programming patterns are going to be more or less problematic in Rust.

As for your question:

  • mut (as in let mut x =) is unimportant for safety. It’s very lightly enforced, and can be worked around (e.g. let x = 1; let mut x = x;).
  • &mut vs & is the real deal about immutability. Note that in Rust immutable data doesn’t exist. Theoretically all values in Rust are mutable. The mutability/immutability in Rust is only about access to the data: you’re either given read-only shared access, or exclusive mutable access.

#6

Thanks for your feedback
There is anther way to make x mut without moving it ?
There is a way to change & to &mut ?


#7

There is no way to change the mutability of a value or binding. It is possible to create a new binding and move the value into the new binding. It is not possible to safely convert a & into a &mut.


#8

You also can’t fully change &mut into &.

    let mut x = 0;
    let r1:&i32 = &mut x;
    let _r2 = &x; // error[E0502]: cannot borrow `x` as immutable 
                  // because it is also borrowed as mutable
    //let _r2 = r1; // copy of ref is allowed
    println!("{}",r1); // (to make sure NLL does not drop early)

“Unique” helps make more sense out of the situation rather than mutability.


#9

No, I don’t think it’s possible to change mutability of a variable without moving it.

There’s no direct, safe way to change & to &mut. However, you can go from a shared reference to exclusive reference via Mutex (i.e. get &mut T from &Mutex<T>).


#10

I’m a bit embarrassed to suggest a solution as a beginner but it’s burning in me :expressionless: , So what do you think on solution like that. (I know there is a lot of use cases , but this is only a suggestion :slightly_smiling_face:)

let mut p = Person {
    name: "Moshe".into(),
    age: 29,
};

// insted point directly to the name, ref related to the p
let name = p.get_name_mut() relate p;

p.name = "change the ref".into();

// it will be ok because the name not point directy to the name. think the name is p.name
*name = "Bob".into();

// p available here, because there is still only one &mut and this is p (name is only related ref)
*p.age = 35;

#11

This is an example of mutable aliasing, so no it won’t work


#12

What the problem with that performance or safety ?
when i wrapping the code with unsafe the error still exist, there is a way to close the borrow checker for specific code area ?


#13

Safety, if this is allowed it will cause bugs. These bugs will be more pronounced in multi thread programs (in the form of data races).


Unsafe doesn’t change any of the exosting rules of rust it simply enables calling unsafe functions, implement unsafe traits, or dereference a raw pointer. I recommended looking into the rust nomicon before diving into unsafe.

https://doc.rust-lang.org/nomicon/


#14

@KrishnaSannasi Thanks for your feedback and patience, I’m very curious to know if there is a plan to solve the problem in a safe way


#15

No problem! I love helping out.


I remember there was some talk about views into structs that would allow you to borrow multiple fields, but that would have to be setup manually or need to have verbose syntax, but I don’t think there was much else.


Views could probably be done as a proc macro now that I think of it