(I have previously asked this on the New to Julia category, but I think might rather belong here)
There are a few things somehow all related about the following situation concerning mutability to which I can not find a clear answer.
Consider a custom type
struct Single # or make mutable?
a::Int
b::Int
# ... and more
end
that itself appears in:
struct DataCollection
structure::Vector{Single}
rawdata::Vector{Array} # not same length as structure
end
We now expect minor modifications such as:
function foo!(dc::DataCollection, w::Int)
# change dc.structure[w].a
# change rawdata[dc.structure[w].a]
end
Further, we expect applications such as:
dc1 = DataCollection() # make some random DataCollection
foo!(dc1,1) # ... and more operations
dc2 = copy(dc1) # in some way, possibly deepcopy
foo!(dc1,1) # ... but do not change dc2 in any way
foo!(dc2,1) # ... but do not change dc1 in any way
What I have so far considered are:
- make
Single
immutable, and letfoo!
do something likedc.structure[w] = Single(new_a,old_b,...)
→ thecopy
operation then only needs to be a shallow copy - make
Single
mutable, and letfoo!
do something likedc.structure[w].a = new_a
→ thecopy
then needs to do adeepcopy
of.structure
- make
Single
mutable, but still do something likedc.structure[w] = Single(new_a,old_b,...)
Question (a): When Single
is immutable
, is there even a difference between = copy(dc.structure)
and = deepcopy(dc.structure)
? In a short test, at least given many fields in Single
, shallow copying a Vector
of mutable Single
’s is indeed significantly faster. This would maybe make (3) much better than (1). The documentation somewhat states that mutable types are hardcopied, though I wonder a bit why, and anyway some more magic apparently happens that I unfortunately do not fully understand.
Question (b): Assuming shallow copies of immutable types are actually cheap, then approach (2) still avoids possibly very frequent reinitializations of Single
type objects. It is also more flexible when dealing with Single
in yet other functions as one can do things like for s in dc.structure; s.a = 1; end
. As approach (2) has some advantages, is there some usual approach to best bookkeep these deepcopies at a minimum?
Question (c): Are there strict counterexamples how any of (1), (2) or (3) can cause other, serious drawbacks or pitfalls (meaning non-generic this is bad style arguments)
Question (d): Only approach (2) would in principle also allow to make .structure
a Tuple
in the sense that changing part of it does not necessitate copying the whole Tuple
. But is this even beneficial?
Question (e): If .structure
instead needs to be Dict
as some bar!
may remove some entry; how does delete!(dc.structure, w)
depend on the mutability of Single
?