user6467981
user6467981

Reputation:

How can I make only certain struct fields mutable?

I have a struct:

pub struct Test {
    pub x: i32,
    pub y: i32,
}

I'd like to have a function that mutates this — easy:

pub fn mutateit(&mut self) {
    self.x += 1;
}

This makes the entire struct mutable for the duration of the function call of mutateit, correct? I only want to mutate x, and I don't want to mutate y. Is there any way to just mutably borrow x?

Upvotes: 17

Views: 9767

Answers (2)

Corvus
Corvus

Reputation: 8049

You can't directly do what you are asking, but it is possible that when you are in the situation that you feel like you need this then perhaps the immutable parts of your struct ought to be references not owned values. Since they will be immutable borrows, their contents are protected. This often logically fits the situation - there must be a reason to protect those fields, presumably because they aren't really "yours" to mess around with.

Changing your example to use String so that we can't have any implicit copy going on, the consider the following:

#[derive(Debug)]
pub struct Test<'a> {
    pub x: String,
    pub y: &'a String,
}

impl<'a> Test<'a> {
    pub fn mutateit(&mut self) {
        self.x.insert(0, '#');
    }
}

fn main() {
    let mut foo = Test{x: "Hello".to_string(), y: &"World".to_string()};
    // foo.y.insert(0, '+');  // This won't work!  I can't mutate y because it is borrowed inside Test
    
    let yref = foo.y;  // If y wasn't behind a & this would cause a partial move

    println!("{:?}", foo);
    println!("{:?}", yref);    
    
    foo.mutateit();    // The borrow into yref would block this mutate, except its a reference not a borrow.
    
    println!("{:?}", foo);
    println!("{:?}", yref);  // So my borrow lifetime is fine.
}

This solution will force you to confront the question of who ultimately should own y, but you probably know the answer to that deep down, if you feel you need to protect it.

Upvotes: 0

ljedrz
ljedrz

Reputation: 22163

Citing The Book:

Rust does not support field mutability at the language level, so you cannot write something like this:

struct Point {
    mut x: i32, // This causes an error.
    y: i32,
}

You need interior mutability, which is nicely described in the standard docs:

use std::cell::Cell; 

pub struct Test {
    pub x: Cell<i32>,
    pub y: i32
}

fn main() {
    // note lack of mut:
    let test = Test {
        x: Cell::new(1), // interior mutability using Cell
        y: 0
    };

    test.x.set(2);
    assert_eq!(test.x.get(), 2);
}

And, if you wanted to incorporate it in a function:

impl Test {
    pub fn mutateit(&self) { // note: no mut again
        self.x.set(self.x.get() + 1);
    }
}

fn main() {
    let test = Test {
        x: Cell::new(1),
        y: 0
    };

    test.mutateit();
    assert_eq!(test.x.get(), 2);
}

Upvotes: 19

Related Questions