Are immutable struct really immutable?

Hi,

I recently realised that it is possible to edit the entries of an immutable struct as long as they are arrays. For example, by using the code:

struct MyStruct
    Y::Array{Float64,1}
end

# Define A
A = MyStruct(ones(10));

# Update A.Y
A.Y .+= rand(10);

I would like to understand why this is permitted. Working with an immutable struct, I would have expected to get an error (or at least a warning).

Would you please clarify that?

3 Likes

The immutability isn’t recursive, but instead a property of the type itself… So its children are free to be mutable, hence you can put a mutable type (Vector in this case) into an immutable type and happily mutate them.

5 Likes

Right, but what is the advantage of a mutable struct vs an immutable struct if you can still mutate its elements? (I understand it does not work for all types, but it does work for many).

Your struct Y is indeed immutable. It’s really just the pointer to an array that lives somewhere else on the heap, and that pointer can’t change (i.e. it can’t point to a different array). You can however modify the values in the array (to an extent), since it’s not actually in your struct.

So while you can write A.Y .+= rand(10), you cannot write A.Y = rand(10), since that would be create a new array and try to change what Y points to.

The array that Y points to lives on the heap, not the stack, and you don’t get the advantages that come with living on the stack.

7 Likes

That you can’t mutate it’s fields?
try:

A.Y = rand(10);

Thank you! This is clearer now.

However, I still have a doubt. Is there a way to recursively apply the immutability to struct elements?

Maybe try using an SArray (from the StaticArrays package) rather than an Array.

1 Like

Note that while StaticArrays are indeed static and immutible, they are designed primarily for linear algebra efficiency on small arrays, not necessarily for “protecting” their contents. You might notice unnecessarily long compile times if you use them.

Julia, being true to its open nature, does not have any concept of “privacy”, even the immutability of struct is more for memory management than for immutability for its own sake.

If you want, you can build a custom immutable array type really easily

struct LockArray{T,N,V<:AbstractArray{T,N}} <: AbstractArray{T,N}
    data::V
end
 
Base.size(v::LockArray) = size(v.data)
Base.getindex(v::LockArray{T,N}, i::Vararg{Int,N}) where {T,N} = v.data[i...]        
function Base.setindex!(v::LockArray{T,N}, x, i::Vararg{Int,N}) where {T,N}
    throw(ArgumentError("Don't touch my array!!"))                 
end
julia> v = LockArray(rand(3,3,3));

julia> typeof(v)
LockArray{Float64,3,Array{Float64,3}}

julia> v[1,1,1] = Inf
ERROR: ArgumentError: Don't touch my array!!
Stacktrace:
 [1] setindex!(::LockArray{Float64,3,Array{Float64,3}}, ::Float64, ::Int64, ::Int64, ::Int64) at /home/msavas200/src/scrap.jl:9
 [2] top-level scope at none:0

More likely however, you’re much better off just not worrying about it at all.

3 Likes

Thank you!

StaticArrays might be useful for part of what I am doing (a forecasting package). However, I still want to make some of the elements of my structures to be private - at least for the sake of debugging.

It is probably a silly question, but: why do you need to define Base.size and Base.getindex? Isn’t Base.setindex! enough to make it private?

It’s just the minimal interface to define an AbstractArray type. (In this case you are basically just telling it that the field you want to access is called data.)

Seriously though, nobody does this for debugging, I encourage you to save yourself some grief and just not worry about it. Be free!

You probably don’t want to use StaticArrays for forecasting, they are really designed for small arrays. If you try using them on giant arrays, the compiler will melt your CPU. (Though, depending on what you are doing, you might have an application for large Arrays of StaticArrays.)

An immutable struct can be mutated because typeof(X) is mutable, so typeof(X).mutable = true works. But… don’t do this :laughing:.

7 Likes

@ExpandingMan Thank you for the suggestions. I might be overthinking it!

@ChrisRackauckas Yeah I do not like it either, it looks odd! :laughing:

1 Like

Just FYI, I asked a similar question to this on StackOverflow when I was first starting out with Julia.

2 Likes