Julia immutable struct with mutable list

Viewed 248

Here's some broken code:

struct NumberedList
  index::Int64
  values::Vector{Int64}

  NumberedList(i) = new(i, Int64[])
end

function set_values!(list::NumberedList, new_values::Vector{Int64})
  list.values = new_values
end

# ---
mylist = NumberedList(1)
set_values!(mylist, [1, 2, 3])

I don't want to do either of these:

  1. I could declare the NumberedList as a mutable struct (making everything mutable)
  2. I could replace set_values! with something like this (copy all the values):
function set_values!(list::NumberedList, new_values::Vector{Int64})
  for i in new_values
      push!(list.values, i)
  end
end

But I'd like to make index immutable, but allow values to be assigned to.


Other notes:

1 Answers

It depends on what exactly you want to mutate.

  1. If you really want to reassign the field values::Vector{Int64} itself, then you have to use a mutable struct. There's no way around that, because the actual data of the struct changes when you reassign that field.
  2. If you use an immutable struct with a values::Vector{Int64} field, it means that you cannot change which array is contained, but the array itself is mutable and can change its elements (which are not stored in the struct). In this case, you really do have to copy values to it from external arrays, like your example code (though I would point out that your code did not reset the array to an empty one). I personally think this would be cleaner:
function set_values!(list::NumberedList, new_values::Vector{Int64})
  empty!(list.values) # reset list.values to Int64[]
  append!(list.values, new_values)
end
  1. The thread you linked talks about using Base.Ref. Base.Ref is pretty much THE way to make a field of an immutable struct indirectly act like a mutable field. It works like this: the field cannot change which RefValue{Vector{Int64}} instance is contained, but the instance itself is mutable and can change its reference (again, not stored in the struct) to any Int64 array. You have to use indexing values[] to get to the array, though:
struct NumberedList
  index::Int64
  values::Ref{Vector{Int64}}

  NumberedList(i) = new(i, Int64[])
end

function set_values!(list::NumberedList, new_values::Vector{Int64})
  list.values[] = new_values # "reassign" different array to Ref
end

# ---
mylist = NumberedList(1)
set_values!(mylist, [1, 2, 3])
Related