Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mutability in fields for structs in Rust

I'm still new with Rust but I have doubts about how mutability works for fields in structs. Specifically how we can modify fields that originally were immutable. For example:

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

impl Point {
    fn new(x: isize, y: isize) -> Self {
        Self { x, y }
    }
    fn set_x(&mut self, new_x: isize) {
        self.x = new_x;
    }
}

struct Line {
    p: Point,
    q: Point,
}

impl Line {
    fn new(p: Point, q: Point) -> Self {
        Self { p, q }
    }
    fn set_x_in_p(&mut self, new_x: isize) {
        self.p.set_x(new_x);
    }
}

fn main() {
    // Both x and y are immutable
    let p = Point::new(0, 0);
    let q = Point::new(1, 1);

    // Line IS mutable
    let mut line = Line::new(p, q);

   // Modifying point p (originally immutable) with a new x 
    line.set_x_in_p(999);
}

Instead with references we cannot

   let x = 3;
   let y = &mut x; // does not work because x originally is immutable

So, how does it work? Thanks.

like image 783
alqacer Avatar asked Apr 27 '26 08:04

alqacer


1 Answers

In your example, p and q are indeed immutable, but then you move them into a Line instance with the constructor, since they're passed by value and don't implement Copy to enable implicit copying. This means that the original bindings (p and q) are no longer valid (the compiler will prevent you from using them) and the values are instead accessible only via the mutable line binding, which allows mutating its members. Essentially, it's not the values that are mutable, but rather their bindings. For example, the following code can be used to re-bind a value to change its mutability:

let x = String::from("hello world"); // using String as an example of a non-Copy type

let mut x = x; // this shadows the previous binding with a mutable one of the same name
x.make_ascii_uppercase(); // we can now mutate the string
let x = x; // shadow the mutable binding with an immutable one

println!("Result: {}", x);

This example works because we have direct control over the value, and can move/bind it as desired. Introducing references would limit what could be done - for example, the following examples wouldn't work:

let x = String::from("hello world");
let x_ref = &x; // create an immutable reference to x
let mut x_mut = x; // error - we can't move x while it's borrowed
let x_mut_ref = &mut x; // error - we can't create a mutable reference while any other references exist

I'd recommend reading the ownership and moves page of Rust by example, which explains it pretty well.

like image 100
apetranzilla Avatar answered Apr 29 '26 00:04

apetranzilla



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!