Initializing Array of Arrays with undef gives UndefRefError?

Hi all,

I am initializing an Array of Arrays with undef. Why does it give me an UndefRefError when accessing it, whereas an Array with undef does not? Also, did some behavior change? I seem to remember initializing Array of Arrays before and never having a UndefRefError before.

a = Array{Int64}(undef, 10)
b = Array{Array{Int64}}(undef, 10)

a[1] # No error
b[1] # UndefRefError

PS: performance issues or best practices asides. It seems that using fill to initialize Array of Arrays might be better, but I’m just wondering what’s happening in the above case.

When you use Vector{T}(undef, n), the uninitialized elements of the vector will be filled with random data (whatever happens to be in memory) if T is an isbits type. If T is not an isbits type, then you will get an UndefRefError if you try to access an uninitialized element, since the element hasn’t been initialized with a pointer to an instance of the non-isbits type.

julia> isbitstype(Int)
true

julia> Vector{Int}(undef, 1)
1-element Array{Int64,1}:
 4588932080

julia> isbitstype(String)
false

julia> Vector{String}(undef, 1)
1-element Array{String,1}:
 #undef

julia> struct A
           x::Int
       end

julia> isbitstype(A)
true

julia> Vector{A}(undef, 1)
1-element Array{A,1}:
 A(4438511664)

julia> mutable struct B
           x::Int
       end

julia> isbitstype(B)
false

julia> Vector{B}(undef, 1)
1-element Array{B,1}:
 #undef
4 Likes

It seems that using fill to initialize Array of Arrays might be better

Watch out for the potential pitfall. See the example in Arrays · The Julia Language

1 Like

As @Paul_Soderlind mentioned, fill is probably not the right tool, unless you want every field to have the same vector. An alternative is using a list comprehension:

b = [Vector{Int64}() for _ = 1:10]

But there is nothing wrong with creating a uninitialized Vector and then assign a inner Vector to each position before accessing it.

3 Likes

Can you tell me how to do this? I’ve created an array of vectors and trying to assign value to one of them but getting the same error as above.

z= Array{Vector{Float64}}(undef, 10, 100)
z[1][1] = [1.,2.,3.]

which gives the error.

UndefRefError: access to undefined reference
julia> z = Array{Vector{Float64}}(undef, 10, 100);

julia> z[1] = [1., 2., 3.]
3-element Vector{Float64}:
 1.0
 2.0
 3.0
1 Like

Thanks! I should have written z[1,1]!

1 Like

Experimenting with 3d and mutable struct (notisbits)

 julia> mtx = Matrix{Vector{SomeStruct}}(undef, 3, 3)
3×3 Matrix{Vector{SomeStruct}}:
 #undef  #undef  #undef
 #undef  #undef  #undef
 #undef  #undef  #undef

julia> mtx[1]
ERROR: UndefRefError: access to undefined reference

What’s the solution?

What did you expect to happen there?

I want to do

julia> push!(mtx[1], SomeStruct(1)) 

But this throws the UndefRefError too

You need to first initialize the array at every position:

julia> struct SomeStruct x::Int end

julia> mtx = Matrix{Vector{SomeStruct}}(undef, 3, 3);

julia> for i in eachindex(mtx)
           mtx[i] = SomeStruct[]
       end

julia> push!(mtx[1], SomeStruct(1))
1-element Vector{SomeStruct}:
 SomeStruct(1)

This is more succinct:

julia> mtx = [ SomeStruct[] for _ in 1:3, _ in 1:3 ]
3×3 Matrix{Vector{SomeStruct}}:
 []  []  []
 []  []  []
 []  []  []

julia> push!(mtx[1], SomeStruct(1))
1-element Vector{SomeStruct}:
 SomeStruct(1)

julia> mtx
3×3 Matrix{Vector{SomeStruct}}:
 [SomeStruct(1)]  []  []
 []               []  []
 []               []  []
1 Like

Thanks a lot. Was trying to avoid loops looks like no way.

I’m curious why you were trying to avoid loops. This is a common heuristic from programmers coming from other languages since loops in those languages may be slow. Often in Julia, a properly written loop is often the fastest approach.

Given the criteria of no loops, we can use map:

julia> map(_->SomeStruct[], CartesianIndices((3,3)))
3×3 Matrix{Vector{SomeStruct}}:
 []  []  []
 []  []  []
 []  []  []

This is essentially the same as the list comprehension above

2 Likes

Purely for readability purposes, no other reason. Initializing a matrix (only once) doesn’t have to be too efficient, just readable so that was why. I agree tho, when it comes to performance I’ve found loops generally perform much better than map et al.

This also works (combining previous suggestions):

julia> [ SomeStruct[] for _ in CartesianIndices((3,3)) ]
3×3 Matrix{Vector{SomeStruct}}:
 []  []  []
 []  []  []
 []  []  []

1 Like

There really is not much practical difference thanks to the compiler. This is almost purely stylistic.

julia> f() = map(_->SomeStruct[], CartesianIndices((3,3)))
f (generic function with 1 method)

julia> g() = [ SomeStruct[] for _ in CartesianIndices((3,3)) ]
g (generic function with 1 method)

julia> @btime f()
  239.900 ns (10 allocations: 560 bytes)
3×3 Matrix{Vector{SomeStruct}}:
 []  []  []
 []  []  []
 []  []  []

julia> @btime g()
  239.467 ns (10 allocations: 560 bytes)
3×3 Matrix{Vector{SomeStruct}}:
 []  []  []
 []  []  []
 []  []  []