Mutating a flag in a nonmutating struct

Option A: Use const on array fields inside a mutable struct

mutable struct StochasticSettings{T}
    remove_scha_forces :: Bool
    clean_start_gradient_centroids :: Bool
    clean_start_gradient_fcs :: Bool
    initialized :: Bool
    const original_force :: Array{T}
    const original_fc_gradient :: Array{T}
end

Option B: Embed a mutable struct inside an immutable wrapper, and flatten fields with ForwardMethods.jl

using ForwardMethods

mutable struct StochasticSettings
    remove_scha_forces :: Bool
    clean_start_gradient_centroids :: Bool
    clean_start_gradient_fcs :: Bool
    initialized :: Bool
end

struct StochasticParam{T}
    settings :: StochasticSettings
    original_force :: Array{T}
    original_fc_gradient :: Array{T}
end

@define_interface StochasticParam interface = properties delegated_fields = (settings,)

x = StochasticParam(...)
x.remove_scha_forces = true  # OK

Option C: Use Base.RefValue

struct StochasticSettings{T}
    remove_scha_forces :: Base.RefValue{Bool}
    clean_start_gradient_centroids :: Base.RefValue{Bool}
    clean_start_gradient_fcs :: Base.RefValue{Bool}
    initialized :: Base.RefValue{Bool}
    original_force :: Array{T}
    original_fc_gradient :: Array{T}
end

x = StochasticParam(...)
x.remove_scha_forces[] = true

A related discussion on using Base.RefValue inside immutable structs:
https://discourse.julialang.org/t/ref-t-vs-base-refvalue-t/127886

Option D: Using Accessors.jl

using Accessors

struct StochasticSettings{T}
    remove_scha_forces :: Bool
    clean_start_gradient_centroids :: Bool
    clean_start_gradient_fcs :: Bool
    initialized :: Bool
    original_force :: Array{T}
    original_fc_gradient :: Array{T}
end

s  = StochasticSettings(...)
s2 = @set s.initialized = true
s2.original_force === s.original_force  # true

The arrays are not deep-copied.

I would choose Option B. But it’s difficult for me to evaluate the performance of different options.

4 Likes