Views of arrays inside a mutable struct

Hi, I’m encountering some unexpected behaviour when using views of arrays inside of a mutable struct. Here is the basic structure:

using Parameters
@with_kw mutable struct Grid
  prim::Array{Float64, 3} = zeros(Float64, 14, 1, 2)
  rho::Matrix{Float64} = @view prim[:, :, 1]
  p::Matrix{Float64} = @view prim[:, :, 2]
end

In a separate function, I do

function init_sod!(g::Grid)
  @. g.rho[3:7, :] = 1.0
  @. g.rho[8:12, :] = 0.125
  @. g.p[3:7, :] = 1.0
  @. g.p[8:12, :] = 0.1
  return g
end

After calling

g = Grid()
g = init_sod!(g)

I find that the array ‘prim’ is still initialised to zero. I also tested this in the REPL by creating Grid() and assigning values to ‘rho’ and ‘p’ outside of the function (i.e. not assigning the returned Grid from the function call) and got the same behaviour. ‘g.rho’ and ‘g.p’ are correctly set, but ‘g.prim’ does not have the values. I don’t understand this, since assigning these views when these members are outside of the mutable struct (global scope) works as expected:

prim = zeros(Float64, 14, 1, 2)
rho = @view prim[:, :, 1]
p = @view prim[:, :, 2]

@. rho[3:7] = 1.0
@. rho[8:12] = 0.125
@. p[3:7] = 1.0
@. p[8:12] = 0.1

Both ‘prim’ and the arrays (‘rho’, ‘p’) have the new values. How can I replicate this behaviour with the original setup, i.e. when the arrays are inside a mutable struct?

I think the issue is that the fields rho and p of your Grid structure are of type Matrix, so the views get converted to that type upon construction. This means a new copy is made, and therefore mutating it will not change prim. It works on the REPL because there you do not enforce a type on rho and p, and so they are truly views. If you really want to have rho and p as views of prim on Grid, they should be of SubArray type (with possibly a bunch of parametric arguments).

But alternatively, you may want to have only prim as a field in Grid, and have getters, like
rho(g::Grid) = view(g.grid,:,:,1) that will behave as you want…

1 Like

If you define

using Parameters
@with_kw mutable struct Grid
  prim::Array{Float64, 3} = zeros(Float64, 14, 1, 2)
  rho = @view prim[:, :, 1]
  p = @view prim[:, :, 2]
end

then you get your desired behaviour. The problem is when you declare the type of rho and p as a Matrix, the view gets promoted to match that type so a copy of prim is made.

1 Like

You could also use:

1 Like