Why `const` fields in a `mutable struct` instead of `mutable` field in ordinary `struct`?

The documentation praises immutable structs:

Composite objects declared with struct are immutable; they cannot be modified after construction. This may seem odd at first, but it has several advantages:

  • It can be more efficient. Some structs can be packed efficiently into arrays, and in some cases the compiler is able to avoid allocating immutable objects entirely.
  • It is not possible to violate the invariants provided by the type’s constructors.
  • Code using immutable objects can be easier to reason about.

So, struct Thing field end creates a type whose instances will be immutable. You have to explicitly opt into mutability using mutable struct Thing field end.

The next section of the documentation says:

In cases where one or more fields of an otherwise mutable struct is known to be immutable, one can declare these fields as such using const

This lets me define a mutable struct with some const fields:

mutable struct I_am_mut
   mutate_me::Int
   const immutable::Int
end

Here, I have to explicitly opt into immutability.


Why this dissonance:

  • Structs are fully immutable. I have to opt into full mutability by using mutable struct. There’s no partial mutability for structs.
  • mutable structs are fully mutable. I can opt into partial immutability by using const.

Why not make struct “partially mutable”, similar to how mutable struct can be partially immutable? Like this:

struct PartiallyMut
   immutable::Int
   mutable mutate_me::Int
end

With this design, mutability is always opt-in, with a gradual scale:

  1. struct is immutable by default: full immutability.
  2. Parts of a struct can be explicitly marked mutable: partial mutability.
  3. The entire struct can be marked mutable with mutable struct: full mutability.

Today’s mutable struct with const fields could’ve been a basic struct with mutable fields.

This also makes mutable struct a generalization of making individual fields mutable.


Does using mutable structs with const fields provide additional benefits? Why was such a choice made?

1 Like

If you have an immutable struct it’s nice to be able to count on it being immutable.

There was some discussion on the PR which implemented the new functionality after the same thing was brought up.

4 Likes

From this comment:

In contrast, if you start with a mutable struct nothing special is needed to make one field const; you can just disallow mutating it.

Indeed, const fields in a mutable struct seem to be much simpler to implement.

I also agree with “a mutable struct with some fields marked const is still mutable, while the opposite is not true (immutable struct with mutable field is no longer immutable)”.


The thing is, I was just trying to make a struct with exactly one mutable field and found my code littered with const fields which looked ugly to me, so I wondered why it was designed this way and of course didn’t find the PR discussion…

1 Like

An easy way if you just need one field is to use inner mutability:

julia> struct MyStruct
          immutable::Int
          reference::Ref{Int}
       end

julia> st = MyStruct(7, Ref(8))
MyStruct(7, Base.RefValue{Int64}(8))

julia> st.reference[] = 9
9

julia> st
MyStruct(7, Base.RefValue{Int64}(9))

5 Likes

IMO the best short answer is that a struct with some fields mutable and some fields const is a mutable struct. If we had had this feature before 1.0, it seems entirely possible to me that we wouldn’t have mutable struct at all and you would have to opt-in to mutability for each mutable field.

Note that is likely worse for performance, as the days won’t be stored inline

1 Like

I’m pretty sure this is the main reason but I’m not sure if it’s documented anywhere. It seems like a good performance tip or maybe just part of the const docstring