Mutable field in immutable type

I sometimes need to change the value in a single field of an instance of an immutable type. This is especially useful when an instance of this type is spread around different places and I need to update one of its values. This way the update occurs everywhere this instance sits, because it’s the same instance.
The way I accomplish this is by wrapping that specific field in a Vector:

julia> immutable A
           x::Int
           y::Vector{Int} # wrap it
       end
julia> a = A(1,[1])
# a lives in all sorts of places
julia> b = Dict(:b => a)
julia> c = [a, a, b]
julia> println(a, b, c) # the `y`field is equal to 1
A(1,[1])Dict(:b=>A(1,[1]))Any[A(1,[1]),A(1,[1]),Dict(:b=>A(1,[1]))]
julia> a.y[1] = 2
julia> println(a, b, c) # the `y`field is equal to 2, everywhhere!
A(1,[2])Dict(:b=>A(1,[2]))Any[A(1,[2]),A(1,[2]),Dict(:b=>A(1,[2]))]

Is there a better way to do this?

Use a mutable type that wraps all the fields you need to mutate. If you only have one, you can abuse Base.RefValue.

2 Likes

Sorry, not sure I understand what you mean:

Use a mutable type that wraps all the fields you need to mutate.

Wrap them in what, a Vector?

If you only have one, you can abuse Base.RefValue.

Could you please specify how I’d “abuse” Base.RefValue?

Thanks!

For the abuse:

immutable A
x::Int
y::Base.RefValue{Int}
end

Why call it abuse? Because there is an ongoing discussion about what Ref/RefValue should be used for…

For the wrapping:

immutable A
x::Int
mutable1::Int
mutable2::Int
end

should become:

type MyMutables
mutable1::Int
mutable2::Int
end
immutable A
x::Int
mutables::MyMutables
end
1 Like

Sorry for reactivating this old post but Simon’s example doesn’t seem to work in 0.7/1.0. If I do

struct A
    x::Int
    y::Base.RefValue{Int}
end

a = A(1, 1)

I get the message ERROR: MethodError: Cannot convert an object of type Int64 to an object of type Base.RefValue{Int64}.

Has the syntax changed or is there a better way now to reset the value of a non-array field in an immutable struct?

a = A(1, Ref(1))
3 Likes

Is the use of Base.RefValue still considered an abuse? Or, should I consider it a performant/permanent solution? Thanks!

2 Likes

I am not quite sure what the abuse is here, except for using an unexported symbol from Base. In any case, this is only necessary if one wants to restrict the type, construction can be done with Ref, eg for

struct A{T1,T2}
    x::T1
    y::T2 # a container like a Ref 
end

A(1, Ref(2))

If the user does not want to define a type for a mutable wrapper, a 0-dimensional array is also an option:

struct A{T1,T2}
    x::T1
    y::Array{T2,0}
end

a = A(1, fill(2))
a.y[] = 3