# Mutable scalar in immutable object: the best alternative?

**URL:** https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401
**Category:** New to Julia
**Created:** [March 17, 2021, 5:43pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401 "2021-03-17T17:43:46Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [March 17, 2021, 5:43pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/1 "2021-03-17T17:43:46Z")

</div>

I found this [old thread](https://discourse.julialang.org/t/mutable-field-in-immutable-type/3103) about the same thing, but that was before 1.0. Actually while writing this post I figured out most of the details… now I will post it anyway.

If one has an immutable struct with many fields, and wants one of the scalar fields to be mutated, some workarounds involve defining it as a one-element array, a zero-element array, or using `Ref`. A small example is shown below.

It seems that `Ref` is faster than the other options. I didn’t expect that in particular relative to an `MVector{1,Float64}`. Anyway, if anyone has something to comment on that, I appreciate.

```julia
using BenchmarkTools, StaticArrays

struct A{T}
  val::T
end

#
# Test with Ref or 0-element array (no index)
#
function test_ref!(v)
  s = 0.
  @inbounds for i in eachindex(v)
    v[i].val[] = v[i].val[] + 0.1
    s += v[i].val[]
  end
  s
end

#
# Test with vectors (first element)
#
function test_vec!(v)
  s = 0.
  @inbounds for i in eachindex(v)
    v[i].val[1] = v[i].val[1] + 0.1
    s += v[i].val[1]
  end
  s
end

# Run

N = 10_000

T = Vector{Float64}
v = A{T}[A(rand(1)) for i in 1:N];
print(" Vec: "); @btime test_vec!($v)

T = Array{Float64,0}
v = A{T}[A{T}(fill(rand())) for i in 1:N];
print(" Array0: "); @btime test_ref!($v)

T = MVector{1,Float64}
v = A{T}[A(rand(T)) for i in 1:N];
print(" MVec: "); @btime test_vec!($v)

T = Base.RefValue{Float64}
v = A{T}[A{T}(Ref(rand())) for i in 1:N];
print(" Ref: "); @btime test_ref!($v)

```

Result:

```julia
 Vec: 22.370 μs (0 allocations: 0 bytes)
 Array0: 22.558 μs (0 allocations: 0 bytes)
 MVec: 23.943 μs (0 allocations: 0 bytes)
 Ref: 18.344 μs (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

### Author: ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)
#### Post date: [March 17, 2021, 5:53pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/2 "2021-03-17T17:53:42Z")

</div>

I am usually using `Setfield.jl` in this case. It requires to change coding style, i.e.

```julia
f!(x)

```

should be changed to

```julia
x = f!(x)

```

And function `f!` should return mutated version of an object.

I haven’t encountered yet a case, where such change is impossible, and it solved the problem of using mutable scalars.

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [March 17, 2021, 5:54pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/3 "2021-03-17T17:54:19Z")

</div>

> [@lmiq](#):
>
> zero-element array

Nitpick: it’s “zero-_dimensional_ array”.

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [March 17, 2021, 6:10pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/4 "2021-03-17T18:10:09Z")

</div>

> [@Skoffer](#):
>
> using `Setfield.jl`

Apparently this option has to be used with some caution:

```julia
using BenchmarkTools, Setfield

struct A{T}
  val::T
end

#
# Test with Setfield - using pairs
#
function test_set1!(v)
  s = 0.
  @inbounds for (i,el) in pairs(v)
    @set! el.val = el.val + 0.1
    v[i] = el
    s += el.val
  end
  s
end

#
# Test with Setfield, updating directly
#
function test_set2!(v)
  s = 0.
  @inbounds for i in eachindex(v)
    @set! v[i].val = v[i].val + 0.1
    s += v[i].val
  end
  s
end

N = 10_000
T = Float64
v = A{T}[A{T}(rand()) for i in 1:N];
print(" @set1: "); @btime test_set1!(v0) setup = (v0=deepcopy($v))
print(" @set2: "); @btime test_set2!(v0) setup = (v0=deepcopy($v))

```

Result:

```julia
julia> print(" @set1: "); @btime test_set1!(v0) setup = (v0=deepcopy($v))
 @set1: 10.029 μs (0 allocations: 0 bytes)
6030.586662602187

julia> print(" @set2: "); @btime test_set2!(v0) setup = (v0=deepcopy($v))
 @set2: 67.330 ms (20000 allocations: 763.70 MiB)
6030.586662602187

```

The first option works great, but it seems that particular care has to be taken in the way we write the loop.

Also, it seems that `v[i] = el` is replacing the complete struct in that position of vector `v`. I would guess that at some point (if the struct is large enough) that becomes slow? (like static arrays become unworthy for sizes larger than some limit). I tried to test that but I didn’t reach that limit, and apparently the performance was not dependent on the size of the struct fields (immutable ones, like tuples, if arrays I would not expect any difference, since arrays are not copied).

@Skoffer , sorry bothering you. If you have any comment on this, please let me know.

---

<div class="post-metadata">

### Author: ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)
#### Post date: [March 17, 2021, 6:35pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/5 "2021-03-17T18:35:30Z")

</div>

Another option is:

1. Use a `mutable struct`
2. Write methods to read the fields of that struct and to set the one field you actually want to change
3. Ensure that you use those methods, rather than directly interacting with the struct fields

The advantage of this is that it requires no Ref wrapper; the disadvantage is that it remains possible for a user to reach into your object and do something unexpected with the internal fields.

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [March 17, 2021, 6:38pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/6 "2021-03-17T18:38:43Z")

</div>

> [@rdeits](#):
>
> 1. Use a `mutable struct`

But if I have a struct with many fields, having the whole struct as mutable might have a performance penalty, doesn’it? I am thinking about a particular case where one of the fields is a scallar and has to be mutated only.

---

<div class="post-metadata">

### Author: ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)
#### Post date: [March 17, 2021, 6:48pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/7 "2021-03-17T18:48:35Z")

</div>

> [@lmiq](#):
>
> But if I have a struct with many fields, having the whole struct as mutable might have a performance penalty, doesn’it?

The only reason I can think of would be [layout optimization internal changes (support pointers inlining/unboxing into parents/codegen) [disabled] by vtjnash · Pull Request #33886 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/33886) (new in 1.5). It’s certainly worth actually doing a benchmark, though.

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [March 17, 2021, 7:38pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/8 "2021-03-17T19:38:21Z")

</div>

An idea: use a mutable struct, but overload Base.setproperty! to choose by yourself which specific fields are (im)mutable:

```julia
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.5.4 (2021-03-11)
 _/ |\ __'_|_|_|\__'_| |
|__/ |

julia> mutable struct A
               immut::Int
               mut::Float64
       end

julia> obj = A(3, 3.14)
A(3, 3.14)

julia> obj.immut = 7
7

julia> import Base.setproperty!

julia> function setproperty!(arg::A, s::Symbol, new)
               s === :immut && error("access to immut is not allowed")
               invoke(setproperty!, Tuple{Any, Symbol, Any}, arg, s, new)
       end
setproperty! (generic function with 7 methods)

julia> obj.immut = 7
ERROR: access to immut is not allowed
Stacktrace:
 [1] setproperty!(::A, ::Symbol, ::Int64) at ./REPL[5]:2
 [2] top-level scope at REPL[6]:1
 [3] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.4/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288

julia> obj.mut = 5.9
5.9

julia> obj
A(7, 5.9)

julia> obj.immut = 9
ERROR: access to immut is not allowed
Stacktrace:
 [1] setproperty!(::A, ::Symbol, ::Int64) at ./REPL[5]:2
 [2] top-level scope at REPL[9]:1
 [3] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.4/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288

```

I’m not sure how well this optimizes, though.

EDIT: Another idea:

```julia
julia> setproperty!(::A, ::Symbol, ::Any) = error("use getters and setters to access A objects")
setproperty! (generic function with 7 methods)

```

and then define some getters and setters. EDIT: actually, being able to define meaningful setters after this would probably require something like a wrapped type, I think. I’m out of my depth 😃

---

<div class="post-metadata">

### Author: ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)
#### Post date: [March 18, 2021, 1:59pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/9 "2021-03-18T13:59:32Z")

</div>

I have added the `mutable struct` option and the `@set!` option to the tests. In this minimal example using `Setfield` with `@set!` turns out to be faster than the other alternatives, but the way one iterates over the elements is very important (I might be doing something wrong when using `@set!` while iterating over indexes). The other alternatives are not as sensible to the way one is iterating:

> **Code**
>
> ```julia
> using BenchmarkTools, StaticArrays, Setfield
> 
> struct A{T}
> val::T
> end
> 
> mutable struct M{T}
> val::T
> end
> 
> #
> # Test with Ref
> #
> function test_ref_index!(v)
> s = 0.
> @inbounds for i in eachindex(v)
> v[i].val[] = v[i].val[] + 0.1
> s += v[i].val[]
> end
> s
> end
> 
> function test_ref_pairs!(v)
> s = 0.
> @inbounds for (i,el) in pairs(v)
> el.val[] = el.val[] + 0.1
> s += el.val[]
> v[i] = el
> end
> s
> end
> 
> #
> # Test with vectors
> #
> function test_vec_index!(v)
> s = 0.
> @inbounds for i in eachindex(v)
> v[i].val[1] = v[i].val[1] + 0.1
> s += v[i].val[1]
> end
> s
> end
> 
> function test_vec_pairs!(v)
> s = 0.
> @inbounds for (i,el) in pairs(v)
> el.val[1] = el.val[1] + 0.1
> s += el.val[1]
> v[i] = el
> end
> s
> end
> 
> #
> # Test with Setfield
> #
> function test_set_index!(v)
> s = 0.
> @inbounds for i in eachindex(v)
> @set! v[i].val = v[i].val + 0.1
> s += v[i].val
> end
> s
> end
> 
> function test_set_pairs!(v)
> s = 0.
> @inbounds for (i,el) in pairs(v)
> @set! el.val = el.val + 0.1
> v[i] = el
> s += el.val
> end
> s
> end
> 
> # With mutable struct
> function test_mut_index!(v)
> s = 0.
> @inbounds for i in eachindex(v)
> v[i].val = v[i].val + 0.1
> s += v[i].val
> end
> s
> end
> 
> function test_mut_pairs!(v)
> s = 0.
> @inbounds for (i,el) in pairs(v)
> el.val = el.val + 0.1
> v[i] = el
> s += el.val
> end
> s
> end
> 
> # Run
> 
> N = 10_000
> 
> T = Float64
> v = M{T}[M{T}(rand()) for i in 1:N];
> print(" mut_index: "); @btime test_mut_index!($v)
> print(" mut_pairs: "); @btime test_mut_pairs!($v)
> 
> T = Float64
> v = A{T}[A{T}(rand()) for i in 1:N];
> print(" @set_index: "); @btime test_set_index!($v)
> print(" @set_pairs: "); @btime test_set_pairs!($v)
> 
> T = Vector{Float64}
> v = A{T}[A(rand(1)) for i in 1:N];
> print(" Vec_index: "); @btime test_vec_index!($v)
> print(" Vec_pairs: "); @btime test_vec_pairs!($v)
> 
> T = Array{Float64,0}
> v = A{T}[A{T}(fill(rand())) for i in 1:N];
> print(" Array0_index: "); @btime test_ref_index!($v)
> print(" Array0_pairs: "); @btime test_ref_pairs!($v)
> 
> T = MVector{1,Float64}
> v = A{T}[A(rand(T)) for i in 1:N];
> print(" MVec_index: "); @btime test_vec_index!($v)
> print(" MVec_pairs: "); @btime test_vec_pairs!($v)
> 
> T = Base.RefValue{Float64}
> v = A{T}[A{T}(Ref(rand())) for i in 1:N];
> print(" Ref_index:: "); @btime test_ref_index!($v)
> print(" Ref_pairs: "); @btime test_ref_pairs!($v)
> 
> ```

Results:

```julia
# mutable struct
 mut_index: 15.854 μs (0 allocations: 0 bytes)
 mut_pairs: 18.482 μs (0 allocations: 0 bytes)
# Setfield
 @set_index: 69.037 ms (20000 allocations: 763.70 MiB) # something wrong?
 @set_pairs: 10.027 μs (0 allocations: 0 bytes) # FASTER
# Standard vector with 1 element
 Vec_index: 22.507 μs (0 allocations: 0 bytes)
 Vec_pairs: 22.157 μs (0 allocations: 0 bytes)
# Array of dimension 0
 Array0_index: 22.971 μs (0 allocations: 0 bytes)
 Array0_pairs: 22.650 μs (0 allocations: 0 bytes)
# Static mutable array of one element
 MVec_index: 20.970 μs (0 allocations: 0 bytes)
 MVec_pairs: 21.036 μs (0 allocations: 0 bytes)
# RefValue
 Ref_index:: 18.291 μs (0 allocations: 0 bytes)
 Ref_pairs: 21.491 μs (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

### Author: ![JianghuiDu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jianghuidu/32/8028_2.png) [@JianghuiDu](https://discourse.julialang.org/u/JianghuiDu)
#### Post date: [April 26, 2021, 4:41pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/10 "2021-04-26T16:41:44Z")

</div>

Why not using mutable struct with in an immutable struct?

```julia
mutable struct M{T}
  val::T
end

#
struct B{T}
    val::M{T}
end

# mutabl within immutable
function test_mut_immut_index!(v)
  s = 0.
  @inbounds for i in eachindex(v)
    v[i].val.val = v[i].val.val + 0.1
    s += v[i].val.val
  end
  s
end

function test_mut_immut_pairs!(v)
  s = 0.
  @inbounds for (i,el) in pairs(v)
    el.val.val = el.val.val + 0.1
    v[i] = el
    s += el.val.val
  end
  s
end

T = Float64
v = B{T}[B{T}(M{T}(rand())) for i in 1:N];
print(" mut_immut_index: "); @btime test_mut_immut_index!($v)
print(" mut_immut_pairs: "); @btime test_mut_immut_pairs!($v)

```

Results on my computer

```julia
julia> print(" mut_immut_index: "); @btime test_mut_immut_index!($v)
 mut_index: 15.900 μs (0 allocations: 0 bytes)
2.9915904161157984e8

julia> print(" mut_immut_pairs: "); @btime test_mut_immut_pairs!($v)
 mut_pairs: 18.600 μs (0 allocations: 0 bytes)
5.658470416076994e8

```

versus `Ref`

```julia
julia> print(" Ref_index:: "); @btime test_ref_index!($v)
 Ref_index:: 14.800 μs (0 allocations: 0 bytes)
3.2040796745854414e8

julia> print(" Ref_pairs: "); @btime test_ref_pairs!($v)
 Ref_pairs: 17.000 μs (0 allocations: 0 bytes)
6.090409674543456e8

```

This is redundant for this simple example of only one element in a struct, but if your struct is large and contains main mutable/immutable elements it could be an option to use a sub mutable struct and sub immutable struct.

---

<div class="post-metadata">

### Author: ![younes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/younes/32/208506_2.png) [@younes](https://discourse.julialang.org/u/younes)
#### Post date: [May 29, 2024, 3:18pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/11 "2024-05-29T15:18:31Z")

</div>

Three years later… I’m wondering if it’s _correct_ to say that the **safest** way to **guarantee** partial immutability is overriding `Base.setproperty!` like @nsajko mentioned

---

<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: [May 29, 2024, 3:33pm UTC](https://discourse.julialang.org/t/mutable-scalar-in-immutable-object-the-best-alternative/57401/12 "2024-05-29T15:33:40Z")

</div>

Since v1.8, Julia supports `const` annotations within mutable structs to make certain fields immutable. [See the docs for more](https://docs.julialang.org/en/v1/manual/types/#Mutable-Composite-Types).

Note that there are still subtle differences between an (immutable) `struct` with mutable fields (e.g., with types `Base.RefVal` or `Array`) and a `mutable struct` with `const` fields. One can carefully consider their situation to decide which is appropriate.

Note that overriding `Base.setproperty!` does not _guarantee_ immutability, as one can still use `Base.setfield!` (which is a special builtin function and cannot be overloaded) to modify entries. A `const` annotation on a field stops mutation at the level of `Base.setfield!` so cannot be circumvented (by any means I am aware of).
