Access to undefined reference: But not always

why in the case of the setindex!() function does the error not arise?
That is, I understand why this behavior was established, but I’m curious to understand how, behind the scenes, the two situations are distinguished.

julia> v=Vector{UInt8}(undef,3)
3-element Vector{UInt8}:
 0xb0
 0x91
 0xfe

julia> vv=Vector{Vector{UInt8}}(undef,3)
3-element Vector{Vector{UInt8}}:
 #undef
 #undef
 #undef

julia> vs=Vector{String}(undef,3)
3-element Vector{String}:
 #undef
 #undef
 #undef

julia> v[1]
0xb0

julia> v[1]=0xff
0xff

julia> vv[1]
ERROR: UndefRefError: access to undefined reference
Stacktrace:
 [1] getindex(A::Vector{Vector{UInt8}}, i1::Int64)
   @ Base .\essentials.jl:13
 [2] top-level scope
   @ c:\Users\sprmn\.julia\environments\v1.9.0\array5.jl:83

julia> vv[1]=[0xff]
1-element Vector{UInt8}:
 0xff

julia> vs[1]
ERROR: UndefRefError: access to undefined reference
Stacktrace:
 [1] getindex(A::Vector{String}, i1::Int64)
   @ Base .\essentials.jl:13
 [2] top-level scope
   @ c:\Users\sprmn\.julia\environments\v1.9.0\array5.jl:85


What are the contraindications to result, for example, , something like this

julia> vs[1]
#undef[string]

and to ensure that, if I use it as a parameter of the copy(vs[1],...) function, it doesn’t give me the error?

Mutable types like Vector{Int} cannot be stored inline in an array; their undef value is a special marker, notifying that there is no object stored there. For String, much the same is true, due to their variable size (although not being mutable).

Immutable values like UInt8 though are stored inline, and because they are immutable and all possible bitpatterns are valid for them, they can be stored inline. An undef value can thus be any arbitrary value of that type.

Do note that reading from an uninitialized array is still not safe to do - this is technically undefined behavior.

Thanks for the precise clarifications.

If my need is to write in that undefined location

`copyto!(vv[1],1, ...)`

how can I do?
Should location pointers be used?
which?
how?

Writing there is fine; just regular indexing assignment will do.

yes, but I can’t write “freely” inside.
My OP’s main question was just about whether setindex() works and getindex() doesn’t.

julia> copyto!(vv[2],1,[0x2],1,1)
ERROR: UndefRefError: access to undefined reference

Well copyto!(vv[1], 1, ...) will not work because there is nothing at vv[1].

Youl would need to initialize vv[1] as follows.

julia> vv = Vector{Vector{UInt8}}(undef, 3)
3-element Vector{Vector{UInt8}}:
 #undef
 #undef
 #undef

julia> vv[1] = Vector{UInt8}(undef, 4)
4-element Vector{UInt8}:
 0xd0
 0x08
 0x2f
 0x2e

julia> vv
3-element Vector{Vector{UInt8}}:
    [0xd0, 0x08, 0x2f, 0x2e]
 #undef
 #undef

julia> copyto!(vv[1], 1:4)
4-element Vector{UInt8}:
 0x01
 0x02
 0x03
 0x04

Certain.
In this way, as in other possible.
But why can’t the “system” do it automatically as it does other things behind the scenes?
something like

vv = Vector{Vector{UInt8}(undef, 4)}(undef, 3)

or better

vv = Vector{Vector{UInt8}(undef)}(undef, 3)

Well Julia is not really do anything automatically.

The difference you observed in the original is that UInt8 is a primitive. In this case, Julia let’s us look at uninitialized memory.

For mutable types that cannot be inlined, Julia has to set the “undef” type. Under the hood, this is a null pointer. When a mutable type is initialized in the array, then the pointer points to some actual place in memory representing the mutable type.

If you want to create a vector of vectors and have the inner vectors declared, then the syntax is as follows.

julia> vv = Vector{UInt8}[Vector{UInt8}(undef, 3) for i in 1:3]
3-element Vector{Vector{UInt8}}:
 [0x00, 0xff, 0xff]
 [0x20, 0x94, 0x7a]
 [0x20, 0x94, 0x7a]
1 Like

Using the @edit macro I found that the two functions getindex() and setindex() are based on two different Core functions, which, first of all, do a boundary check (I took it for granted by setting the first parameter to true ) then they do the rest which in the case of getindex results in the error message.
But the arrayref function I believe is written in C and I’m afraid it’s not “observable”

@edit vs[1]="aaa"
@edit vs[1]
Core.arrayset(true, vs, convert(String,"aaa")::String, 1)
Core.arrayref(true, vs, 1) # ERROR: UndefRefError: access to undefined reference

The relevant C code is here:

If you really want to hack around to see how this currently works, see my package Undefs.jl:

julia> using Undefs

julia> vs = Vector{String}(undef, 5)
5-element Vector{String}:
 #undef
 #undef
 #undef
 #undef
 #undef

julia> jla_vs = Undefs.JLArrays.JLArray(vs)
Undefs.JLArrays.JLArray{Nothing}(Ptr{Nothing} @0x0000021d02771718, 0x0000000000000005, 0x9804, 0x0008, 0x00000000, 0x0000000000000005, 0x0000000000000005, nothing)

julia> unsafe_load(Ptr{Ptr{Nothing}}(jla_vs.data), 1)
Ptr{Nothing} @0x0000000000000000

julia> vs[1] = "Hello world"
"Hello world"

julia> unsafe_load(Ptr{Ptr{Nothing}}(jla_vs.data), 1)
Ptr{Nothing} @0x0000021d017152d0

julia> vs[1]
"Hello world"

julia> unsafe_string(Ptr{UInt8}(unsafe_load(Ptr{Ptr{Nothing}}(jla_vs.data), 1)+8))
"Hello world"

julia> unsafe_store!(Ptr{Ptr{Nothing}}(jla_vs.data), C_NULL)
Ptr{Ptr{Nothing}} @0x0000021d02771718

julia> vs
5-element Vector{String}:
 #undef
 #undef
 #undef
 #undef
 #undef

julia> vs[1]
ERROR: UndefRefError: access to undefined reference
Stacktrace:
 [1] getindex(A::Vector{String}, i1::Int64)
   @ Base .\essentials.jl:13
 [2] top-level scope
   @ REPL[67]:1
1 Like