How to make a field immutable?

Access fields unmutably.

Let's say I have a struct:

struct Foo
{
    pub a : A,
    pub b : B,
}

and a function:
fn bar(a : &A, b : &mut B).

I want Foo.a to be immutable. However, if I were to make it private and write an immutable accessor, then I couldn't call:
bar(&foo.a, &mut foo.b)
anymore, since the accessor
fn get_a(&self) -> &A { &self.a }
would borrow the entire Foo on a call.

How do I work around this without creating a function which borrows every field (some mutable and some immutable, depending on which fields should remain unmutated)?

2 Likes

One way would be to not have it pub and have a getter method, I think.

Oh, you've it at the end, my bad for not reading completely before replying.

There's an accepted RFC for immutable fields, but AFAIK the implementation hasn't landed yet.

There are some workarounds, like you could have a

struct FooLoan<'foo> {
    pub a: &'foo A,
    pub b: &'foo mut B,
}

impl Foo {
    fn loan(&mut self) -> FooLoan<'_> { ... }
}
4 Likes

it always surprised me that Rust fields aren't immutable by default, given that's how identifiers work everywhere else in the language ¯_(ツ)_/¯

and yeah, while you can make it non-pub and write a getter, that only enforces immutability on the external consumers of your class, not the internal developers (future you!) – so I'm very much looking forward to readonly struct fields

1 Like

Well… “everywhere else” lack of mut is not a serious restriction: you may always move from immutable variable to mutable one and do as many modifications as you want… that wouldn't work with fields, there lack mutability would actually mean something more strict.

oh true, icwym. The same should apply to struct fields tho, right? mut if you can change it in-place, nothing if you can't; but either way, if you want, you can still move out of the field to have ownership and mutability, leaving a partially moved struct. So mut is only applying when you're not moving the field out

The workaround you can always do is something like

pub struct Foo {
    a: A,
    b: B,
}
pub struct FooView<'a, 'b> {
    pub a: &'a A,
    pub b: &'b B,
}
impl Foo {
    pub fn view(&self) -> FooView<'_, '_> {
        FooView { a: &self.a, b: &self.b }
    }
}

So people can get borrows to both if they want.

3 Likes

One could argue that if a field a must uphold invariants that depend on other (private) fields in its enclosing struct, while another field b in the same struct is freely mutable, then b arguably does not belong in that struct.

1 Like