Now I’m going to describe what happens when you use Union{Stuff, Nothing}
as the element type of the array. Let’s create the array, and probe the pointers.
julia> s = Vector{Union{Stuff,Nothing}}(nothing, 5)
5-element Vector{Union{Nothing, Stuff}}:
nothing
nothing
nothing
nothing
nothing
We can then populate the first three elements of the array once again.
julia> s[1] = Stuff(1, [], [])
Stuff(1, Float64[], String[])
julia> s[2] = Stuff(2, [3.0], ["a"])
Stuff(2, [3.0], ["a"])
julia> s[3] = Stuff(3, [4.0, 5.0], ["a", "b"])
Stuff(3, [4.0, 5.0], ["a", "b"])
julia> s
5-element Vector{Union{Nothing, Stuff}}:
Stuff(1, Float64[], String[])
Stuff(2, [3.0], ["a"])
Stuff(3, [4.0, 5.0], ["a", "b"])
nothing
nothing
Probing the pointers, we see they are now spaced 8 bytes apart rather than 24 bytes apart. We also cannot load the pointer directly.
julia> pointer(s, 2) - pointer(s, 1)
0x0000000000000008
julia> pointer(s, 3) - pointer(s, 2)
0x0000000000000008
julia> pointer(s, 4) - pointer(s, 3)
0x0000000000000008
julia> ptr = pointer(s)
Ptr{Union{Nothing, Stuff}} @0x00007f9304503518
julia> unsafe_load(ptr)
ERROR: pointerref: invalid pointer type
I’m going to speculate that the pointer points to another pointer. Let’s see what happens.
julia> ptr = Ptr{Ptr{Nothing}}(ptr)
Ptr{Ptr{Nothing}} @0x00007f9304503518
julia> unsafe_load(ptr, 1)
Ptr{Nothing} @0x00007f93047b15d0
julia> unsafe_load(ptr, 2)
Ptr{Nothing} @0x00007f93047c8f10
julia> unsafe_load(ptr, 3)
Ptr{Nothing} @0x00007f93047e4a10
julia> unsafe_load(ptr, 4)
Ptr{Nothing} @0x00007f9307e17008
julia> unsafe_load(ptr, 5)
Ptr{Nothing} @0x00007f9307e17008
Hmm… the last two elements, which are both nothing
are a pointer to the same thing, while the first three differ! Let’s see if we can load the first three elements.
julia> unsafe_load(Ptr{Stuff}(unsafe_load(ptr, 1)))
Stuff(1, Float64[], String[])
julia> unsafe_load(Ptr{Stuff}(unsafe_load(ptr, 2)))
Stuff(2, [3.0], ["a"])
julia> unsafe_load(Ptr{Stuff}(unsafe_load(ptr, 3)))
Stuff(3, [4.0, 5.0], ["a", "b"])
Do not try to load elements 4 and 5 as Stuff
! Julia will crash.
Julia must know what is Stuff
and what is nothing
.
julia> unsafe_pointer_to_objref(unsafe_load(ptr, 1))
Stuff(1, Float64[], String[])
julia> unsafe_pointer_to_objref(unsafe_load(ptr, 2))
Stuff(2, [3.0], ["a"])
julia> unsafe_pointer_to_objref(unsafe_load(ptr, 3))
Stuff(3, [4.0, 5.0], ["a", "b"])
julia> unsafe_pointer_to_objref(unsafe_load(ptr, 4))
julia> unsafe_pointer_to_objref(unsafe_load(ptr, 5))
We did not need to tell Julia that elements 1, 2, and 3 were Stuff
and that elements 4 and 5 were nothing
. Here’s a hint about how that works:
julia> unsafe_load(Ptr{Ptr{Nothing}}(unsafe_load(ptr, 1) - 8))
Ptr{Nothing} @0x00007f74acef7442
julia> unsafe_load(Ptr{Ptr{Nothing}}(unsafe_load(ptr, 2) - 8))
Ptr{Nothing} @0x00007f74acef7442
julia> unsafe_load(Ptr{Ptr{Nothing}}(unsafe_load(ptr, 3) - 8))
Ptr{Nothing} @0x00007f74acef7442
julia> unsafe_load(Ptr{Ptr{Nothing}}(unsafe_load(ptr, 4) - 8))
Ptr{Nothing} @0x00007f74a1d132c3
julia> unsafe_load(Ptr{Ptr{Nothing}}(unsafe_load(ptr, 5) - 8))
Ptr{Nothing} @0x00007f74a1d132c3
Also note that
julia> sizeof(nothing)
0