32

In Rust, you don't specify mutability inside a struct, but it is inherited from the variable binding. That's great, but is it possible to force a field to be always immutable, even when the root is mutable?

Something like this hypothetical syntax:

struct A {
    immut s: Shape, // immutable by design
    bla: Bla, // this field inheriting (im)mutability
}
let mut a = make_a();
a.s = x/*...*/; // illegal

This would help to maintain nice semantic restrictions in a program, just like Java's final does (in a very limited way).

Also, we could imagine this kind of struct having some non-owning references to internal immutable data, taking advantage of this immutability...

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Lionel Parreaux
  • 1,115
  • 1
  • 9
  • 22
  • Well, if you make it `priv` and don't modify it in any code inside the same module, it *is* effectively immutable. And of course one can always replace `a` wholesale (`a = make_another_a();`), which may or may not be a problem. –  May 19 '14 at 18:41

6 Answers6

25

It's impossible to have immutability of a single field. That was an option in an ancient version of Rust (think before 0.8), but it was dropped because the rules confused a LOT of people. How was it confusing, you might ask? Think about it like this: if a field is declared mutable and struct is declared mutable and the reference used was an immutable reference (&) then the field is _______.

The best, as Lily Ballard noted, is that you can declare your Shape field as private and make a getter method using impl A {...}.

mod inner {
    pub struct A {
        s: i32, // can't be seen outside of module
        pub bla: i32,
    }

    impl A {
        pub fn new() -> Self {
            Self { s: 0, bla: 42 }
        }

        pub fn get_s(&self) -> i32 {
            self.s
        }
    }
}
let mut a = inner::A::new();
a.s = 42; // illegal
println!("{}", a.s); // also illegal
println!("{}", a.get_s()); // could be made to serve as a read-only method
error[E0616]: field `s` of struct `main::inner::A` is private
  --> src/main.rs:20:5
   |
20 |     a.s = 42; // illegal
   |     ^^^

error[E0616]: field `s` of struct `main::inner::A` is private
  --> src/main.rs:21:20
   |
21 |     println!("{}", a.s); // also illegal
   |                    ^^^

There is proposition that might drop notions of mutability and immutability completely (you can't say a struct never changes). See Niko's explanation for that change.

Lee SongUn
  • 55
  • 6
Daniel Fath
  • 16,453
  • 7
  • 47
  • 82
  • Okay, I understand it could be confusing. Out of curiosity, what was the actual behaviour when taking an &A and trying to mutate its field? My guess is that it would be impossible since immutability is deep (inherited)... – Lionel Parreaux May 21 '14 at 13:37
  • 1
    Here you go a detailed explanation by kibwen: http://www.reddit.com/r/rust/comments/264d2t/history_question_why_was_mut_from_structsclasses/ – Daniel Fath May 21 '14 at 18:03
  • @LP_ Basically, you had inherited mutability of fields of a struct. E.g. `struct Foo {priv mut stuff}` could be modifed, so for example `let a = &Foo { stuff: 2 }` could be modified using `a.stuff = 3`. You could never guarantee that a field would be immutable, and how could you? – Daniel Fath May 23 '14 at 08:11
17

You can create a struct and only implement the Deref trait for it. Without the DerefMut trait it won't be possible for contained values to be mutated.

https://doc.rust-lang.org/std/ops/trait.Deref.html

This way the compiler will make the member usable as if it's not wrapped in another struct, no need for any written getter method call.

use std::ops::Deref;

/// A container for values that can only be deref'd immutably.
struct Immutable<T> {
    value: T,
}

impl<T> Immutable<T> {
    pub fn new(value: T) -> Self {
        Immutable { value }
    }
}

impl<T> Deref for Immutable<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.value
    }
}
struct Foo {
    bar: Immutable<Vec<u8>>,
    baz: usize,
}

impl Foo {
    pub fn new(vec: Vec<u8>) -> Self {
        Foo {
            bar: Immutable::new(vec),
            baz: 1337,
        }
    }

    pub fn mutate(&mut self) {
        self.bar.push(0); // This will cause a compiler error
    }
}
|
|         self.bar.push(0);
|         ^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `runnable::immutable::Immutable<std::vec::Vec<u8>>`
danthedaniel
  • 439
  • 4
  • 7
  • 2
    If you end up using this approach, remember to implement the same traits for `Immutable` as for `T`, else a user of your library might encounter weird errors when attempting to use a `Immutable` as a `T`. [See this example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=fe86eeacf749a2efa41b499107cfbedd) – Filipe Rodrigues Jul 17 '20 at 12:48
  • You can always just deref to fix that: [example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4650df3d9a68f9a4d176faf0a33e0fa0). But if you want the ergonomics of normal trait use, then you may find it desirable to impl a trait or two on Immutable – danthedaniel Jul 19 '20 at 08:34
  • 1
    This does nothing to stop you from putting a new value into `bar` though. – Alex Sep 19 '22 at 04:33
  • Fair point - and that's actually what the OP asked for. I guess if you combine a private field with a getter and this immutable wrapper struct you'd have a perfect, deeply immutable solution (when used outside of the struct). – danthedaniel Sep 20 '22 at 06:40
4

You can't force immutability on a field. How would the struct mutate its own value when necessary?

What you can do is make the field private and expose a getter method to return a reference to it (or to copy/clone the value).

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • 26
    It doesn't always make sense to be able to mutate data; have you heard about functional programming, for instance? The Java `final` modifier could be considered useless since a simple getter can accomplish the same effect. It's just a matter of making it easy (so that people do it more naturally -- it is always good to avoid useless potential states in code) and clearer (auto-documenting that a value will never change can be useful). – Lionel Parreaux May 21 '14 at 13:59
  • @LP_ I think it should be up to the API user of a struct to decide if they want to mutate or not... if they don't want to, they can hold an immutable reference. – David Callanan Jun 21 '20 at 19:42
  • 3
    @DavidCallanan That really depends on whether the struct has to maintain invariants on its fields that aren't enforced by the type system. For example, `Vec` has a private field `len` and a public getter `fn len(&self)`. Mutating the `len` field would break the safety of `Vec` so it has to be kept read-only. – Lily Ballard Jun 23 '20 at 01:52
  • @LilyBallard Sorry I was talking about structs with public fields only. – David Callanan Jun 23 '20 at 12:08
  • Immutability is an extremely valuable construct. As a rule of thumb, I think that everything should be immutable by default. Mutability should be the exception. – Alkis Mavridis Oct 31 '21 at 08:53
4

Probably the easiest way to achieve this today is by using the readonly crate from prolific developer David Tolnay (author of serde and many others). From the github documentation:

This crate provides an attribute macro to expose struct fields that are readable and writable from within the same module but readable only outside the module.

#[readonly::make]
pub struct S {
    // This field can be read (but not written) by super.
    #[readonly]
    pub(super) readable: i32,

    // This field can be neither read nor written by other modules.
    private: i32,
}
user986730
  • 1,226
  • 1
  • 13
  • 12
1

A Solution could be to have a more general approach:

pub struct Immutable<T> {
    value : T ,
}

impl<T> Immutable<T> {

    pub fn new(value : T) -> Immutable<T> {
        Immutable { value : value }
    }

    pub fn get( &self) -> &T { &self.value }
}

Now it is possible to use the Immutable struct in every case for every other type.

Given this into a module avoids changing the content of the Immutable object. It is still possible to change the variable which holds the Immutable object itself by overwriting it by a new Object but you should notice it by the Immutable::new statement and so you can avoid using it.

1

In many cases an associated constant achieves the desired behaviour:

struct Foo { blah: Blah }

impl Foo {
    const S: Shape = Shape { x: 1, y: 1 };
}

Of course, constants cannot be set on creation, they are set at compile-time. Making the field private as explained in other answers will work if dynamicism is required.

Ibraheem Ahmed
  • 11,652
  • 2
  • 48
  • 54