# Mutable vs immutable struct: modifying an array field

**URL:** <https://discourse.julialang.org/t/mutable-vs-immutable-struct-modifying-an-array-field/94976>\
**Category:** General Usage\
**Tags:** struct, mutable-structure\
**Created:** [February 21, 2023, 7:06pm UTC](https://discourse.julialang.org/t/mutable-vs-immutable-struct-modifying-an-array-field/94976 "2023-02-21T19:06:54Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![jacob-roth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacob-roth/32/1862_2.png) [@jacob-roth](https://discourse.julialang.org/u/jacob-roth)\
**Post date:** [February 21, 2023, 7:06pm UTC](https://discourse.julialang.org/t/mutable-vs-immutable-struct-modifying-an-array-field/94976/1 "2023-02-21T19:06:54Z")

</div>

I am confused about the mutable vs immutable struct [documentation](https://docs.julialang.org/en/v1/manual/types/#Composite-Types).

> For composite types, this means that the identity of the values of its fields will never change. When the fields are bits types, that means their bits will never change, for fields whose values are mutable types like arrays, that means the fields will always refer to the same mutable value even though that mutable value’s content may itself be modified.

I think I have a basic question, but it is not quite answered here: [question1](https://discourse.julialang.org/t/structs-mutable-versus-immutable/53889/17) and [question2](https://discourse.julialang.org/t/struct-with-immutable-arrays/36457). It concerns when I should use mutable vs immutable structs.

In my application, I am trying to create a struct containing all fields of interest (that have already been allocated), and then create sub-structs (referring to the already allocated fields) that do particular things to the already allocated elements.

For example, I will make a struct with three fields `a`, `b`, and `c` for already allocated `a,b,c` and then do specific operations to `a` or (`b` and `c`) by defining convenience structs that refer to the already allocated `a,b,c` from my “everything” struct.

```julia
mutable struct Everything{T,VT<:AbstractVector{T}}
  a::Ref{T}
  b::VT
  c::VT
end
mutable struct Parta{T<:AbstractFloat}
  a::Ref{T}
end
function Parta(Ev::Everything)
  return Parta(Ev.a)
end
function (Pa::Parta)()
  Pa.a[] += 1.0
end
mutable struct Partbc{T,VT<:AbstractVector{T}}
  b::VT
  c::VT
end
function Partbc(Ev::Everything)
  return Partbc(Ev.b, Ev.c)
end
function (Pbc::Partbc)()
  Pbc.b .+= 2.0
  Pbc.c .+= 3.0
end

```

Now initializing the example:

```julia
a0 = Ref{Float64}(0.0)
b0 = randn(5)
c0 = randn(5)
a = deepcopy(a0)
b = copy(b0)
c = copy(c0)
Ev = Everything(a, b, c)
Pa = Parta(Ev)
Pbc = Partbc(Ev)
Pa()
Pbc()

```

my understanding is that `Pa` and `Pbc` just point to `a,b,c`, so that’s why we can expect `Pbc.b-b` to be zero but `Pbc.b-b0` to be 2, e.g.,

```julia
julia> hcat(Pbc.b-b, Pbc.b-b0)
5×2 Matrix{Float64}:
 0.0 2.0
 0.0 2.0
 0.0 2.0
 0.0 2.0
 0.0 2.0

```

When I make everything immutable structs, it works the same (as expected)

```julia
struct immEverything{T,VT<:AbstractVector{T}}
  a::Ref{T}
  b::VT
  c::VT
end
struct immParta{T<:AbstractFloat}
  a::Ref{T}
end
function immParta(immEv::immEverything)
  return immParta(immEv.a)
end
function (Pa::immParta)()
  Pa.a[] += 1.0
end
struct immPartbc{T,VT<:AbstractVector{T}}
  b::VT
  c::VT
end
function immPartbc(immEv::immEverything)
  return immPartbc(immEv.b, immEv.c)
end
function (Pbc::immPartbc)()
  Pbc.b .+= 2.0
  Pbc.c .+= 3.0
end
immEv = immEverything(a, b, c)
immPa = immParta(immEv)
immPbc = immPartbc(immEv)
immPa()
immPbc()

julia> hcat(immPbc.b-b, immPbc.b-b0)
5×2 Matrix{Float64}:
 0.0 4.0
 0.0 4.0
 0.0 4.0
 0.0 4.0
 0.0 4.0

```

For this example, I am curious when I should use mutable vs immutable structs. The docs say

> Mutable values, on the other hand are heap-allocated and passed to functions as pointers to heap-allocated values

but I am not sure how `immutable` values are passed to functions (does this happen when calling `immPbc`?). In my case is it a pointer to the (stack?)-allocated values `a,b,c`? It seems that I should use immutable structs here, so I’m curious about a simple modification to this example that shows when I would need to use `mutable` structs.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [February 21, 2023, 7:56pm UTC](https://discourse.julialang.org/t/mutable-vs-immutable-struct-modifying-an-array-field/94976/2 "2023-02-21T19:56:48Z")

</div>

> [@jacob-roth](#):
>
> but I am not sure how `immutable` values are passed to functions (does this happen when calling `immPbc`?). In my case is it a pointer to the (stack?)-allocated values `a,b,c`?

In general, julia uses “pass by sharing”, so if you pass an object (either mutable or immutable), the function semantically has access to the same exact object. How this happens exactly depends on a lot of factors, in part whether the called function was inlined or not. Immutable objects just have the advantage that they are (sometimes) able to be shared by just copying the parts that you actually need (such as an `isbitstype` field - it can only be read after all).

Regardless, _generally_ mutable values are passed by a pointer to the object, because you may modify one of its fields, which must be reflected in all other contexts that have access to that object, be that other functions, closures or other things that have a (for this purpose) permanent lifetime.

Whether the individual fields of e.g. `Everything` are stored inline with an instance of that type or not depends partially on whether or not the type of that field is `isbitstype` - generally, arrays are stored as pointers, as are `Ref`s (since those objects are mutable and changes to their contents must be visible outside any potential objects wrapping them).

> [@jacob-roth](#):
>
> In my application, I am trying to create a struct containing all fields of interest (that have already been allocated), and then create sub-structs (referring to the already allocated fields) that do particular things to the already allocated elements.

Depending on the field type, that may be a bit awkward (and probably suboptimal for performance), since you’ll just end up chasing pointers around. E.g. your `mutable struct Parta` is likely to be stored as a pointer, containing a pointer to the final value that you care about - this is no better than just having a field `a::Everything`, and strictly worse than just passing that `Everything` directly, which is only a single pointer deref (and field offset, which is a constant and will likely be added to the pointer directly for dereferencing).

* * *

In general, your usecase seems a bit rare, or at least I haven’t encountered this use of explicitly wrapping a `Float64` in a `Ref` a lot. To me it seems much more prudent to just have `mutable struct Everything` with an `a::Float64` field, pass that object around and modify the `a` field directly - the struct is already mutable after all and doing it like that is just a single pointer deref, instead of multiple. Maybe I’m missing something?

---

<div class="post-metadata">

**Author:** ![jacob-roth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacob-roth/32/1862_2.png) [@jacob-roth](https://discourse.julialang.org/u/jacob-roth)\
**Post date:** [February 21, 2023, 11:00pm UTC](https://discourse.julialang.org/t/mutable-vs-immutable-struct-modifying-an-array-field/94976/3 "2023-02-21T23:00:54Z")

</div>

Thanks for your reply! When you say

> [@Sukera](#):
>
> this is no better than just having a field `a::Everything`, and strictly worse than just passing that `Everything` directly, which is only a single pointer deref (and field offset, which is a constant and will likely be added to the pointer directly for dereferencing).

do you mean that in general it would be better to create functions like:

```julia
struct Everything{T<:Real,VT<:AbstractVector{T}}
  a::Ref{T}
  b::VT
  c::VT
end
function update_a!(Ev::Everything)
  Ev.a[] += 1.0
end
function update_bc!(Ev::Everything)
  Ev.b .+= 2.0
  Ev.c .+= 3.0
end

```

If so, how does sharing a larger object (with “many” unrelated fields, as in the case of `update_a!`) affect performance? I guess it would be best to write `update_a!` for a `Ref{T}` and then pass it `Ev.a`?

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [February 21, 2023, 11:44pm UTC](https://discourse.julialang.org/t/mutable-vs-immutable-struct-modifying-an-array-field/94976/4 "2023-02-21T23:44:13Z")

</div>

> [@jacob-roth](#):
>
> ` a::Ref{T}`

Note that `Ref{T}` is actually an abstract type. In a struct field, you want a `Base.RefValue{T}` to avoid performance penalties. I.e., note that

```julia-repl
julia> Ref(1234)
Base.RefValue{Int64}(1234)

```

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [February 22, 2023, 8:15am UTC](https://discourse.julialang.org/t/mutable-vs-immutable-struct-modifying-an-array-field/94976/5 "2023-02-22T08:15:32Z")

</div>

> [@jacob-roth](#):
>
> If so, how does sharing a larger object (with “many” unrelated fields, as in the case of `update_a!`) affect performance?

It still works the same conceptually, but again, depending on how this is used, the assembly can look totally different:

> **Everything and the Ref don't escape**
>
> ```julia
> julia> function f()
> e = Everything(Ref(1.0), Float64[], Float64[])
> update_a!(e)
> e.a[]
> end
> f (generic function with 1 method)
> 
> julia> @code_native f()
> .text
> .file	"f"
> .section	.rodata.cst8,"aM",@progbits,8
> .p2align	3 # -- Begin function julia_f_549
> .LCPI0_0:
> .quad	0x4000000000000000 # double 2
> .text
> .globl	julia_f_549
> .p2align	4, 0x90
> .type	julia_f_549,@function
> julia_f_549: # @julia_f_549
> ; ┌ @ REPL[20]:1 within `f`
> .cfi_startproc
> # %bb.0: # %top
> #APP
> mov	rax, qword ptr fs:[0]
> #NO_APP
> mov	rax, qword ptr [rax - 8]
> mov	rax, qword ptr [rax + 16]
> mov	rax, qword ptr [rax + 16]
> #MEMBARRIER
> mov	rax, qword ptr [rax]
> movabs	rax, offset .LCPI0_0
> #MEMBARRIER
> vmovsd	xmm0, qword ptr [rax] # xmm0 = mem[0],zero
> ; │ @ REPL[20]:4 within `f`
> ret
> .Lfunc_end0:
> .size	julia_f_549, .Lfunc_end0-julia_f_549
> .cfi_endproc
> ; └
> # -- End function
> .section	".note.GNU-stack","",@progbits
> 
> ```

> **The Ref escapes**
>
> ```julia
> julia> function f()
> e = Everything(Ref(1.0), Float64[], Float64[])
> update_a!(e)
> e.a
> end
> f (generic function with 1 method)
> 
> julia> @code_native f()
> .text
> .file	"f"
> .globl	julia_f_560 # -- Begin function julia_f_560
> .p2align	4, 0x90
> .type	julia_f_560,@function
> julia_f_560: # @julia_f_560
> ; ┌ @ REPL[22]:1 within `f`
> .cfi_startproc
> # %bb.0: # %top
> sub	rsp, 8
> .cfi_def_cfa_offset 16
> #APP
> mov	rax, qword ptr fs:[0]
> #NO_APP
> mov	rax, qword ptr [rax - 8]
> ; │ @ REPL[22]:2 within `f`
> ; │┌ @ refpointer.jl:136 within `Ref`
> ; ││┌ @ refvalue.jl:10 within `RefValue` @ refvalue.jl:8
> mov	esi, 1136
> mov	edx, 16
> mov	rcx, qword ptr [rax + 16]
> mov	rcx, qword ptr [rcx + 16]
> #MEMBARRIER
> ; │└└
> ; │ @ REPL[22]:1 within `f`
> mov	rcx, qword ptr [rcx]
> #MEMBARRIER
> ; │ @ REPL[22]:2 within `f`
> ; │┌ @ refpointer.jl:136 within `Ref`
> ; ││┌ @ refvalue.jl:10 within `RefValue` @ refvalue.jl:8
> mov	rdi, qword ptr [rax + 16]
> movabs	rax, offset ijl_gc_pool_alloc
> call	rax
> movabs	rcx, 139865608574432
> movabs	rdx, 4611686018427387904
> mov	qword ptr [rax - 8], rcx
> ; │└└
> ; │ @ REPL[22]:3 within `f`
> ; │┌ @ REPL[1]:2 within `update_a!`
> ; ││┌ @ refvalue.jl:57 within `setindex!`
> ; │││┌ @ Base.jl:38 within `setproperty!`
> mov	qword ptr [rax], rdx
> ; │└└└
> ; │ @ REPL[22]:4 within `f`
> pop	rcx
> .cfi_def_cfa_offset 8
> ret
> .Lfunc_end0:
> .size	julia_f_560, .Lfunc_end0-julia_f_560
> .cfi_endproc
> ; └
> # -- End function
> .type	.L_j_const1,@object # @_j_const1
> .section	.rodata.cst8,"aM",@progbits,8
> .p2align	3
> .L_j_const1:
> .quad	0x3ff0000000000000 # double 1
> .size	.L_j_const1, 8
> 
> .section	".note.GNU-stack","",@progbits
> 
> ```

You can ignore the stuff before the `#MEMBARRIER`, that’s just an artifact from GC safepointing and how `@code_native` prints assembly.

Note how there’s a call to `jl_gc_alloc` in the second version to allocate the `Ref`, where the first just returns a constant. So how the performance changes depends on how `Everything` ends up being used in your code, but in general (assuming a `Ref` field for `a` and the allocation for `Everything` can’t be elided and it ends up on the heap), it may look like this:

> **Full output**
>
> ```julia
> julia> struct Everything{T<:Real,VT<:AbstractVector{T}}
> a::Base.RefValue{T}
> b::VT
> c::VT
> end
> 
> julia> function update_a!(Ev::Everything)
> Ev.a[] += 1.0
> end
> update_a! (generic function with 1 method)
> 
> julia> function update_bc!(Ev::Everything)
> Ev.b .+= 2.0
> Ev.c .+= 3.0
> end
> update_bc! (generic function with 1 method)
> 
> julia> function f(e)
> update_a!(e)
> update_bc!(e)
> end
> f (generic function with 1 method)
> 
> julia> code_native(f, (Everything{Float64, Vector{Float64}},))
> .text
> .file	"f"
> .section	.rodata.cst8,"aM",@progbits,8
> .p2align	3 # -- Begin function julia_f_176
> .LCPI0_0:
> .quad	0x3ff0000000000000 # double 1
> .text
> .globl	julia_f_176
> .p2align	4, 0x90
> .type	julia_f_176,@function
> julia_f_176: # @julia_f_176
> ; ┌ @ REPL[8]:1 within `f`
> .cfi_startproc
> # %bb.0: # %top
> sub	rsp, 8
> .cfi_def_cfa_offset 16
> #APP
> mov	rax, qword ptr fs:[0]
> #NO_APP
> mov	rax, qword ptr [rax - 8]
> movabs	rcx, offset .LCPI0_0
> mov	rax, qword ptr [rax + 16]
> mov	rax, qword ptr [rax + 16]
> #MEMBARRIER
> mov	rax, qword ptr [rax]
> #MEMBARRIER
> ; │ @ REPL[8]:2 within `f`
> ; │┌ @ REPL[2]:2 within `update_a!`
> ; ││┌ @ Base.jl:37 within `getproperty`
> mov	rax, qword ptr [rdi]
> ; ││└
> ; ││┌ @ refvalue.jl:56 within `getindex`
> ; │││┌ @ Base.jl:37 within `getproperty`
> vmovsd	xmm0, qword ptr [rax] # xmm0 = mem[0],zero
> ; ││└└
> ; ││┌ @ float.jl:408 within `+`
> vaddsd	xmm0, xmm0, qword ptr [rcx]
> ; ││└
> ; ││┌ @ refvalue.jl:57 within `setindex!`
> ; │││┌ @ Base.jl:38 within `setproperty!`
> vmovsd	qword ptr [rax], xmm0
> ; │└└└
> ; │ @ REPL[8]:3 within `f`
> movabs	rax, offset "j_update_bc!_178"
> call	rax
> pop	rcx
> .cfi_def_cfa_offset 8
> ret
> .Lfunc_end0:
> .size	julia_f_176, .Lfunc_end0-julia_f_176
> .cfi_endproc
> ; └
> # -- End function
> .section	".note.GNU-stack","",@progbits
> 
> ```

The relevant section of which is

```julia
	movabs	rcx, offset .LCPI0_0
; │ @ REPL[8]:2 within `f`
; │┌ @ REPL[2]:2 within `update_a!`
; ││┌ @ Base.jl:37 within `getproperty`
	mov	rax, qword ptr [rdi]
; ││└
; ││┌ @ refvalue.jl:56 within `getindex`
; │││┌ @ Base.jl:37 within `getproperty`
	vmovsd	xmm0, qword ptr [rax] # xmm0 = mem[0],zero
; ││└└
; ││┌ @ float.jl:408 within `+`
	vaddsd	xmm0, xmm0, qword ptr [rcx]
; ││└
; ││┌ @ refvalue.jl:57 within `setindex!`
; │││┌ @ Base.jl:38 within `setproperty!`
	vmovsd	qword ptr [rax], xmm0
; │└└└
; │ @ REPL[8]:3 within `f`
	movabs	rax, offset "j_update_bc!_178"
	call	rax
	pop	rcx
	.cfi_def_cfa_offset 8
	ret

```

So first, the offset of the constant `.LCPI0_0` (which holds `1.0`) is loaded into `rcx`. Then, the pointer of the `Ref` is loaded into `rax`, using the pointer to the `Everything` object passed in via `rdi`. That is then derefed and stored in `xmm0`. Finally, the constant is added (`vdaddsd`) and stored back (`vmovsd`). `update_bc!` did not end up being inlined, so its offset is loaded and the function is called.

Compare this to the same code using a `mutable` struct and no `Ref` at all:

> **Full code**
>
> ```julia
> julia> mutable struct Everything{T<:Real,VT<:AbstractVector{T}}
> a::T
> b::VT
> c::VT
> end
> 
> julia> function update_a!(Ev::Everything)
> Ev.a += 1.0
> end
> update_a! (generic function with 1 method)
> 
> julia> function update_bc!(Ev::Everything)
> Ev.b .+= 2.0
> Ev.c .+= 3.0
> end
> update_bc! (generic function with 1 method)
> 
> julia> function f(e)
> update_a!(e)
> update_bc!(e)
> end
> f (generic function with 1 method)
> 
> julia> code_native(f, (Everything{Float64, Vector{Float64}},))
> .text
> .file	"f"
> .section	.rodata.cst8,"aM",@progbits,8
> .p2align	3 # -- Begin function julia_f_93
> .LCPI0_0:
> .quad	0x3ff0000000000000 # double 1
> .text
> .globl	julia_f_93
> .p2align	4, 0x90
> .type	julia_f_93,@function
> julia_f_93: # @julia_f_93
> ; ┌ @ REPL[4]:1 within `f`
> .cfi_startproc
> # %bb.0: # %top
> sub	rsp, 8
> .cfi_def_cfa_offset 16
> #APP
> mov	rax, qword ptr fs:[0]
> #NO_APP
> mov	rax, qword ptr [rax - 8]
> mov	rax, qword ptr [rax + 16]
> mov	rax, qword ptr [rax + 16]
> #MEMBARRIER
> mov	rax, qword ptr [rax]
> #MEMBARRIER
> ; │ @ REPL[4]:2 within `f`
> ; │┌ @ REPL[2]:2 within `update_a!`
> ; ││┌ @ Base.jl:37 within `getproperty`
> vmovsd	xmm0, qword ptr [rdi] # xmm0 = mem[0],zero
> movabs	rax, offset .LCPI0_0
> ; ││└
> ; ││┌ @ float.jl:408 within `+`
> vaddsd	xmm0, xmm0, qword ptr [rax]
> ; │└└
> ; │ @ REPL[4]:3 within `f`
> movabs	rax, offset "j_update_bc!_95"
> ; │ @ REPL[4]:2 within `f`
> ; │┌ @ REPL[2]:2 within `update_a!`
> ; ││┌ @ Base.jl:38 within `setproperty!`
> vmovsd	qword ptr [rdi], xmm0
> ; │└└
> ; │ @ REPL[4]:3 within `f`
> call	rax
> pop	rcx
> .cfi_def_cfa_offset 8
> ret
> .Lfunc_end0:
> .size	julia_f_93, .Lfunc_end0-julia_f_93
> .cfi_endproc
> ; └
> # -- End function
> .section	".note.GNU-stack","",@progbits
> 
> ```

The relevant part of which is

```julia
; │ @ REPL[4]:2 within `f`
; │┌ @ REPL[2]:2 within `update_a!`
; ││┌ @ Base.jl:37 within `getproperty`
	vmovsd	xmm0, qword ptr [rdi] # xmm0 = mem[0],zero
	movabs	rax, offset .LCPI0_0
; ││└
; ││┌ @ float.jl:408 within `+`
	vaddsd	xmm0, xmm0, qword ptr [rax]
; │└└
; │ @ REPL[4]:3 within `f`
	movabs	rax, offset "j_update_bc!_95"
; │ @ REPL[4]:2 within `f`
; │┌ @ REPL[2]:2 within `update_a!`
; ││┌ @ Base.jl:38 within `setproperty!`
	vmovsd	qword ptr [rdi], xmm0
; │└└
; │ @ REPL[4]:3 within `f`
	call	rax
	pop	rcx
	.cfi_def_cfa_offset 8
	ret

```

Where we can just load the field `a` directly (`vmovsd xmm0, qword ptr [rdi]`) without having to chase an additional pointer through the `Ref`. In a hot loop, that can make a difference, since you have one less potential for a cache miss through the additional pointer load.

> [@jacob-roth](#):
>
> I guess it would be best to write `update_a!` for a `Ref{T}` and then pass it `Ev.a`?

Unless you have a really good (semantic!) reason to require the field `a` to have the ability to live longer than the `Everything` object, wrapping it in a `Ref` like that is in general very likely to be strictly worse than just having a `mutable` struct with no `Ref` at all, since `Float64` is an `isbitstype` (i.e. pointer-free) that is stored inline with what its being wrapped by.

Maybe I’m missing something about how your mental model of julia works though. For example, this part:

> [@jacob-roth](#):
>
> I will make a struct with three fields `a`, `b`, and `c` for already allocated `a,b,c` and then do specific operations to `a` or (`b` and `c`) by defining convenience structs that refer to the already allocated `a,b,c` from my “everything” struct.

doesn’t necessarily make sense depending on the type of `a` since we don’t generally say that we “allocate” a `Float64`. It’s an immutable value, most of the time just being passed via registers (if at all). There is no ability to take a pointer to an immutable value that landed on the stack - or rather, there is no _semantic guarantee_ that a `Ref` to a `Float64` ends up as a pointer to a previously used location on the stack (though it may happen if the compiler deems it safe… the more likely case is for the `Ref` to be heap allocated and tracked by GC though, since it’s mutable and a mutable values that escape the function they’re defined in are guaranteed to be safe when allocated on the heap).

So from that POV, having a “previously allocated `RefValue{Float64}`” kind of begs the question why that `RefValue` needs to exist like that in the first place, when the `Everything` struct seems to be the owner of that value. The convenience structs don’t really gain you anything performance or codesize wise, since they too are either passed as a pointer (if they’re mutable and they escape the scope they’re created in) or are just eliminated entirely (likely to happen if they’re immutable and just hold the `Ref`), at which point either passing the `Everything` or the `Ref`-field directly achieves the same thing, with less code & less mental overhead about which wrapper structs refer to which `Everything` object.

* * *

To circle back around to whether you should use mutable or immutable structs for your `Everything` - to me, there isn’t enough information here to definitively recommend one over another, if any, specifically these two versions:

```julia
struct Everything{T,VT<:AbstractVector{T}}
   a::Base.RefValue{T}
   b::VT
   c::VT
end

mutable struct Everything{T,VT<:AbstractVector{T}}
    a::T
    const b::VT
    const c::VT
end

```

The reason being that these two are expressing different intentions with regards to what `a` means and how it’s used in your program (though personally I’d much prefer the mutable version here, purely due to not wanting to write `ev.a[]` everywhere and I wouldn’t expect the `a` to live longer than `Everything`).
