# Is accessing an \`undef\` array undefined behavior?

**URL:** <https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899>\
**Category:** General Usage\
**Created:** [July 21, 2023, 8:43am UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899 "2023-07-21T08:43:17Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [July 21, 2023, 8:25pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/21 "2023-07-21T20:25:26Z")

</div>

Both approaches are equivalent in terms of avoiding the [billion dollar mistake](https://softwareengineering.stackexchange.com/questions/413149/if-null-is-a-billion-dollar-mistake-what-is-the-solution-to-represent-a-non-ini) - in neither Julia nor Rust can you get a `Null` reference to an object of some type `T` in a safe way. In both Rust and Julia, reading a value from an uninitialized array gives you a (potentially invalid) instance of that type - Rust just explicitly calls that out as undefined behavior, while Julia hasn’t quite defined that it is undefined. In both cases though, you can’t rely anymore on the guarantees a type is supposed to provide, so really, in neither language should it be done at all.

That doesn’t change the fact that at some point, the memory has to transition from “uninitialized” to “initialized”. It’s just a matter of whether there are some situations where it’s required to do that as a user of a language, instead of the language doing it for you (circling back to “do we require arrays to be instantiated with their values initialized or not”). This is quickly going to get very abstract/technical though, so in the interest of not giving an introduction to type theory, I’ll stop here 🙂

---

<div class="post-metadata">

**Author:** ![Keno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/keno/32/285_2.png) [@Keno](https://discourse.julialang.org/u/Keno)\
**Post date:** [July 21, 2023, 8:35pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/22 "2023-07-21T20:35:51Z")

</div>

> [@DNF](#):
>
> Is this undefined behavior?

Yes, though probably not the kind that we’d exploit for optimization. We’d likely change the behavior of it to be defined if the compiler got to the point of being strong enough for this to be a problem (because then it could likely also optimize away the cases where it’s currently slow).

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 21, 2023, 8:48pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/23 "2023-07-21T20:48:35Z")

</div>

What could that look like? What would a stronger compiler be able to do and how is it related to guaranteeing reads of an uninitialized `isbitstype` will always give RAM bits thrown into it? I was under the impression that the current state of affairs is the fastest case, basically not doing any work to check or warn validity.

---

<div class="post-metadata">

**Author:** ![Keno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/keno/32/285_2.png) [@Keno](https://discourse.julialang.org/u/Keno)\
**Post date:** [July 21, 2023, 8:51pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/24 "2023-07-21T20:51:07Z")

</div>

If we defined `undef` to zero-initialize, but the compiler was able to prove (in the common cases we care about) that all valid indices of the array are written to, then the compiler can elide the zero initialization without it ever being semantically visible.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 21, 2023, 9:00pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/25 "2023-07-21T21:00:25Z")

</div>

So in other words, we could define `undef` initialization in the future like `zeros` now, but the compiler could make that like `undef` initialization now if it realizes those zeros are overwritten anyway?

Also shoutout to ArrayAllocators.jl for `calloc` substituting for `undef` for fast zero-initialization in some cases.

---

<div class="post-metadata">

**Author:** ![Keno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/keno/32/285_2.png) [@Keno](https://discourse.julialang.org/u/Keno)\
**Post date:** [July 21, 2023, 9:04pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/26 "2023-07-21T21:04:08Z")

</div>

> [@Benny](#):
>
> So in other words, we could define `undef` initialization in the future like `zeros` now, but the compiler could make that like `undef` initialization now if it realizes those zeros are overwritten anyway?

Yes

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [July 21, 2023, 9:07pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/27 "2023-07-21T21:07:59Z")

</div>

Zero initialization (whether with `calloc` or explicitly filling the array with zeros) doesn’t help us in avoiding the undefined behavior. I can just have a type which must have at least one `1` in its bitpattern and presto, zero initialization is not valid anymore.

Avoiding the undefined behavior is intricately linked to only giving out a reference to arrays whose elements have all been properly initialized, by going through their constructor. There’s no fancy trick around that.

The optimization that would allow removal of the zero initialization is IMO the same one we’d need for proving that the existing use of `undef` is safe.

---

<div class="post-metadata">

**Author:** ![Keno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/keno/32/285_2.png) [@Keno](https://discourse.julialang.org/u/Keno)\
**Post date:** [July 21, 2023, 9:14pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/28 "2023-07-21T21:14:49Z")

</div>

There is a difference between an access being undefined behavior and an access violating constraints that may be imposed by a constructor. There is currently various ways to construct invalid objects, undef is only one of them. We’ve been talking about locking that down to support some of the formal verification ideas, but inner constructor reachability currently does not restrict the set of constructable objects.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [July 21, 2023, 9:17pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/29 "2023-07-21T21:17:51Z")

</div>

That situation seems to be “defined” though. As in it is “defined” to not be valid and will always error.

Base.show(io::IO, ::MIME"text/plain", x::Foo) = x.x == 1 ? print(“Foo”) : error(“Invalid Foo Detected”)

```julia
julia> struct Foo
           x::Int
           Foo() = new(1)
       end

julia> Foo()
Foo

julia> Base.show(io::IO, ::MIME"text/plain", x::Foo) = x.x == 1 ? print("Foo") : error("Invalid Foo Detected")

julia> using ArrayAllocators

julia> Vector{Foo}(calloc, 5)
5-element Vector{Foo}:
 Error showing value of type Vector{Foo}:
ERROR: Invalid Foo Detected

```

In that case, one might want my other package, ArrayInitializers.jl.

```julia
julia> using ArrayInitializers

julia> A = Vector{Foo}(ArrayInitializers.fill!(Foo()), 5)
5-element Vector{Foo}:
 Foo
 Foo
 Foo
 Foo
 Foo

```

I’m not really convinced the Rust situation is much better. You basically just do the same thing, use unsafe, and ask Rust to assume it’s initialized after some point.

> **[MaybeUninit in std::mem - Rust](https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#examples)**
>
> A wrapper type to construct uninitialized instances of \`T\`.

The difference to me seems mostly to be we should have called `undef` to be `unsafeinit` to be very clear.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [July 21, 2023, 9:23pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/30 "2023-07-21T21:23:33Z")

</div>

> [@Keno](#):
>
> There is a difference between an access being undefined behavior and an access violating constraints that may be imposed by a constructor.

I don’t think so. What I’m saying is that if we treat accessing invalid instances that were created via `undef` in an array as undefined behavior, then we must also treat accessing invalid instances that come from a zero initializer as undefined behavior, because both break the semantics imposed by the type the same way. If our compiler ends up treating these two differently, I’d consider that a bug (raw malloc is permitted to give out zeroed pages, so having a difference there sounds extremely suspicious).

> [@mkitti](#):
>
> Base.show(io::IO, ::MIME"text/plain", x::Foo) = x.x == 1 ? print(“Foo”) : error(“Invalid Foo Detected”)

That example still permits construction of the invalid object and only errors once its displayed. That’s way too late and not even close to being safe. You can’t in general check post-creation whether an (immutable) object is valid, because that would mean no longer trusting types to give any guarantees at all. That’s squarely the job of a constructor.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [July 21, 2023, 9:28pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/31 "2023-07-21T21:28:08Z")

</div>

> [@Sukera](#):
>
> That example still permits construction of the invalid object and only errors once its displayed. That’s way too late and not even close to being safe. You can’t in general check post-creation whether an (immutable) object is valid, because that would mean no longer trusting types to give any guarantees at all. That’s squarely the job of a constructor.

I was trying to illustrate your point actually, that we did in fact allow creation of an invalid `Foo`.

Here’s another approach by overriding `Base.zero`. Perhaps there needs to be a more abstract concept of `Base.defaultvalue` for this to make sense?

```julia
julia> using ArrayInitializers

julia> Base.zero(::Type{Foo}) = Foo()

julia> Vector{Foo}(zeroinit, 5)
5-element Vector{Foo}:
 Foo
 Foo
 Foo
 Foo
 Foo

```

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [July 21, 2023, 9:29pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/32 "2023-07-21T21:29:56Z")

</div>

> [@mkitti](#):
>
> Here’s another approach by overriding `Base.zero`. Perhaps there needs to be a more abstract concept of `Base.defaultvalue` for this to make sense?

That example actually constructs valid instances though, by going through the constructor of `Foo` 🙂

The core issue is that “default value” doesn’t make sense for every type, so we can’t use that to make `undef` work for every type.

---

<div class="post-metadata">

**Author:** ![Mo8it](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mo8it/32/32649_2.png) [@Mo8it](https://discourse.julialang.org/u/Mo8it)\
**Post date:** [July 21, 2023, 9:31pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/33 "2023-07-21T21:31:25Z")

</div>

> [@mkitti](#):
>
> I’m not really convinced the Rust situation is much better. You basically just do the same thing, use unsafe, and ask Rust to assume it’s initialized after some point.
> 
> [MaybeUninit in std::mem - Rust](https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#examples)

In Rust, you don’t need an uninitialized state for collections contiguous in memory since `push` is not an expensive ccall like in Julia. You would just use `with_capacity` as shown in the blog post.

`MaybeUninit` is very low level. You would definitely put it behind a safe abstraction. It is not for “end users”.

* * *

Now that the solution was marked as it being undefined behavior, I will remove the note tomorrow. You can reply if you don’t agree with it being removed.

---

<div class="post-metadata">

**Author:** ![Keno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/keno/32/285_2.png) [@Keno](https://discourse.julialang.org/u/Keno)\
**Post date:** [July 21, 2023, 9:43pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/34 "2023-07-21T21:43:04Z")

</div>

> [@Sukera](#):
>
> I don’t think so.

I’m assuming you didn’t mean it that way, but FWIW, this didn’t come across particularly well to me. I was trying to help out here, by giving an authoritative answer to people’s questions. Undefined behavior is a very specific thing, and some things are undefined behavior and some things are not. Could one imagine a world in which having an object that is “invalid” in various senses is undefined behavior, sure, but that’s not how things currently work. You could certainly argue for different semantics, but muddying the waters here is a disservice to the people who asked the question.

> [@Sukera](#):
>
> invalid instances

Julia does not have this concept currently.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [July 21, 2023, 9:52pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/35 "2023-07-21T21:52:18Z")

</div>

> [@Keno](#):
>
> Undefined behavior is a very specific thing, and some things are undefined behavior and some things are not. Could one imagine a world in which having an object that is “invalid” in various senses is undefined behavior, sure, but that’s not how things currently work.

Yes, and I gave what I take to be the common definition in my first post in this thread 🤷 At the very least, it’s not clear at all what the term “undefined behavior” means in the context of julia - the meaning from C doesn’t translate well, because Julia doesn’t quite have the same memory model (exposed to the user of the language) and doesn’t have a standard.

What would you say “undefined behavior” means in Julia, just so we’re all on the same page? I’ve got the feeling that it’s going to differ from what people up top in the thread (or heck, me) think it is.

> [@Keno](#):
>
> Julia does not have this concept currently.

Certainly not exposed semantically, but internally we surely do. That’s why we canonicalize booleans to truly only have the lowest bit set, right?

> <https://github.com/JuliaLang/julia/issues/34909>
>
> Booleans are represented by \`i8\`, but logically represent \`i1\`. Now, if we ever …come into the situation of having a denormalized Bool, like \`reinterpret(Bool, 0x03)\`, we get very confusing behavior. I am not sure whether this is 
> 1. Intentional UB (needs docs)
> 2. A bug
> 3. Not UB, there are coherent semantics (unlikely)
> 
> \`\`\`
> julia\> @noinline h(a,u) = push!(a,reinterpret(Bool, u))
> 
> julia\> a=Set{Bool}(); h(a, 0x03); h(a, 0x03)
> Set(Bool\[1, 1\])
> 
> julia\> a=Set{Any}(); h(a, 0x03); h(a, 0x03)
> Set(Any\[true\])
> 
> julia\> function f(b)
> b==true && 1
> b==false && 2
> 3
> end
> 
> julia\> f(reinterpret(Bool, 0x03))
> 3
> 
> julia\> reinterpret(Bool, 0x03) === true
> true
> \`\`\`
> Especially the last test suggests that this is pretty UB in practice

(as well as related issues/PRs with `reinterpret`, where much the same issue comes up)

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 21, 2023, 10:21pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/36 "2023-07-21T22:21:11Z")

</div>

> [@Sukera](#):
>
> Zero initialization (whether with `calloc` or explicitly filling the array with zeros) doesn’t help us in avoiding the undefined behavior.

I believe zero initialization here did refer to filling an array with proper `Base.zero` elements, not just filling all the bits with 0. That would be different from `calloc` finding zeroed pages.

> [@Keno](#):
>
> > [@Sukera](#):
> >
> > invalid instances
> 
> Julia does not have this concept currently.

That’s interesting. So take my earlier example of a type `NeverZero` whose inner constructor corrects inputs of 0, but I used an `undef` trick to make (instances printed as) `NeverZero(0)`. Is it formally considered an instance of `NeverZero`? `reinterpret(NeverZero, zeros(UInt8, 8))[1] isa NeverZero` certainly is `true`.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [July 21, 2023, 11:30pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/37 "2023-07-21T23:30:12Z")

</div>

> [@Mo8it](#):
>
> Now that the solution was marked as it being undefined behavior, I will remove the note tomorrow. You can reply if you don’t agree with it being removed.

In the other posts, I agreed that _accessing_ an `undef` array that has not been initialized is undefined behavior. I concur with Keno.

I do not agree that using `undef` itself is undefined behavior though, which I believe was your original assertion. This is a different matter. As long as it is followed by an initialization routine, it’s fine. Once the array has been initialized, accessing it is not undefined behavior.

A couple years ago I did put forward a pull request to make `undef` easier to use by making a function `undefs` as an analog to `zeros` or `ones`.

> <https://github.com/JuliaLang/julia/pull/42620>
>
> Create an easy to use function, \`undefs\` that creates an uninitialized array usi…ng similar syntax to \`zeros\` and \`ones\`.
> 
> New users to Julia often use \`zeros\` or \`ones\` to initialize arrays even though initialization may not be needed. Part of the reason is that the syntax of \`zeros\` and \`ones\` is uncomplicated and reminiscent of similar functions in \[other languages\](https://www.mathworks.com/help/matlab/ref/ones.html) and \[frameworks\](https://numpy.org/doc/stable/reference/generated/numpy.zeros.html). Additionally, these functions also have a default type of \`Float64\`.
> 
> To make the creation of uninitialized arrays easier for users new to Julia, the method \`undefs\` is added that mimics the syntax and argument order of \`ones\` and \`zeros\`. Similar to \`ones\` and \`zeros\`, it also has a default type of \`Float64\`.
> 
> While the functionality is redundant with \`Array{T}(undef, dims)\`, except for the \`Float64\` default, or \`similar\`, the syntax is straightforward, does not require the use of curly braces, or the use of an existing array.
> 
> Let's make efficient Julia easier to use with \`undefs\`.
> 
> \`\`\`julia
> julia\> Array{Float64}(undef, 5,5)
> 5×5 Matrix{Float64}:
> 5.0e-324 2.5e-323 7.64061e-316 7.64062e-316 5.0e-323
> 2.0e-323 7.64061e-316 7.64062e-316 4.4e-323 5.4e-323
> 7.6406e-316 7.64061e-316 4.0e-323 4.4e-323 7.64063e-316
> 7.64061e-316 3.0e-323 4.0e-323 7.64063e-316 7.64064e-316
> 2.5e-323 3.5e-323 7.64062e-316 7.64063e-316 0.0
> 
> julia\> undefs(5, 5)
> 5×5 Matrix{Float64}:
> 7.63889e-316 1.22467e-315 5.99379e-316 7.63893e-316 7.64422e-316
> 7.63889e-316 7.64418e-316 7.63891e-316 7.63893e-316 5.99388e-316
> 7.63889e-316 7.63889e-316 7.63892e-316 7.64421e-316 5.99402e-316
> 7.64418e-316 7.64418e-316 7.63892e-316 5.99387e-316 7.63894e-316
> 5.99377e-316 7.64419e-316 7.63892e-316 7.63872e-316 0.0
> 
> julia\> Array{Int}(undef, 5,5)
> 5×5 Matrix{Int64}:
> 0 0 0 0 0
> 0 0 0 0 0
> 0 0 0 0 0
> 0 0 0 0 0
> 0 0 0 0 0
> 
> julia\> undefs(Int, 5, 5)
> 5×5 Matrix{Int64}:
> 0 0 0 0 0
> 0 0 0 0 0
> 0 0 0 0 0
> 0 0 0 0 0
> 0 0 0 0 0
> \`\`\`
> 
> See also \[\`numpy.empty\`\](https://numpy.org/doc/stable/reference/generated/numpy.empty.html)

This was rejected for a number of reasons. Between the discussion there and past discussions, it became clear that `undef` was meant to be relatively difficult to use. In other words, as you put it, it was also not meant for “end users”.

The other point you made is that `undef` was part of a performance recommendation. As far as I can tell all the performance examples using `undef` are followed by initialization, so I do not see any of those as exhibiting undefined behavior either.

> [@Mo8it](#):
>
> In Rust, you don’t need an uninitialized state for collections contiguous in memory since `push` is not an expensive ccall like in Julia. You would just use `with_capacity` as shown in the blog post.

Many of the safe ways of initializing an array are similiar to those in Rust. Essentially use an iterator with a known size. This could be used in a `map` or an array comprehension.

The `push!` kind of paradigm of building an array only really makes sense to me when I do not know the final size of the collection. I’m also not sure what you mean by “expensive ccall”. It’s just a `call` after compilation. The one issue perhaps is that it is difficult to inline this.

If we compile the following function `foo()`.

```julia
julia> function foo()
           A = Int[]
           sizehint!(A, 5)
           push!(A, 1)
       end
foo (generic function with 1 method)

```

The “ccall” just becomes a call in LLVM IR in the same way a C routine would call another C routine. This does not seem extraordinarily expensive unless you are trying to build a very large vector this way in a tight for loop.

```julia
; │┌ @ array.jl:1014 within `_growend!`
    call void inttoptr (i64 140705553961648 to void ({}*, i64)*)({}* nonnull %7, i64 1)

```

> **LLVM IR of foo()**
>
> ``` julia\> @code_llvm foo() ; @ REPL[476]:1 within `foo` ; Function Attrs: uwtable define nonnull {}* @julia_foo_2906() #0 { top: %gcframe2 = alloca [3 x {}*], align 16 %gcframe2.sub = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe2, i64 0, i64 0 %0 = bitcast [3 x {}*]* %gcframe2 to i8* call void @llvm.memset.p0i8.i32(i8* noundef nonnull align 16 dereferenceable(24) %0, i8 0, i32 24, i1 false) %1 = call {}***inttoptr (i64 140705554136352 to {}*** ()*)() #4 ; @ REPL[476]:2 within `foo` ; ┌ @ array.jl:400 within `getindex` ; │┌ @ boot.jl:477 within `Array` %2 = bitcast [3 x {}*]* %gcframe2 to i64* store i64 4, i64* %2, align 16 %3 = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe2, i64 0, i64 1 %4 = bitcast {} **%3 to {}*** %5 = load {} **, {}*** %1, align 8 store {} **%5, {}*** %4, align 8 %6 = bitcast {} ***%1 to {}*** store {} **%gcframe2.sub, {}*** %6, align 8 %7 = call nonnull {}* inttoptr (i64 140705553955840 to {}* ({}*, i64)*)({}* inttoptr (i64 140705037065232 to {}*), i64 0) %8 = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe2, i64 0, i64 2 store {}* %7, {}**%8, align 16 ; └└ ; @ REPL[476]:3 within `foo` ; ┌ @ array.jl:1285 within `sizehint!` call void inttoptr (i64 140705553963920 to void ({}*, i64)*)({}* nonnull %7, i64 5) ; └ ; @ REPL[476]:4 within `foo` ; ┌ @ array.jl:1061 within `push!` ; │┌ @ array.jl:1014 within `\_growend!` call void inttoptr (i64 140705553961648 to void ({}*, i64)*)({}* nonnull %7, i64 1) ; │└ ; │ @ array.jl:1062 within `push!` ; │┌ @ abstractarray.jl:419 within `lastindex` ; ││┌ @ abstractarray.jl:382 within `eachindex` ; │││┌ @ abstractarray.jl:133 within `axes1` ; ││││┌ @ abstractarray.jl:98 within `axes` ; │││││┌ @ array.jl:149 within `size` %9 = bitcast {}* %7 to { i8*, i64, i16, i16, i32 }* %10 = getelementptr inbounds { i8*, i64, i16, i16, i32 }, { i8*, i64, i16, i16, i32 }* %9, i64 0, i32 1 %11 = load i64, i64* %10, align 8 ; │└└└└└ ; │┌ @ array.jl:969 within `setindex!` %12 = add nsw i64 %11, -1 %13 = bitcast {}* %7 to i64** %14 = load i64*, i64 **%13, align 8 %15 = getelementptr inbounds i64, i64* %14, i64 %12 store i64 1, i64* %15, align 8 %16 = load {}*, {}** %3, align 8 %17 = bitcast {} ***%1 to {}** store {}* %16, {}** %17, align 8 ; └└ ret {}* %7 } ```

In Julia, the capacity issue is a bit complicated since the `Array` type is supposed to represent an n-dimensional array, not just a one-dimensional vector. `Vector` is a special case which is allowed to act as a double ended queue.

One Base structure in Julia that does have the explicit concept of capacity is `Channel`:

> Constructs a Channel with an internal buffer that can hold a maximum of size objects of type T.

This is perhaps a bit heavy since it also meant to be thread safe, but perhaps for the very large vector case we are discussing that may be useful.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 21, 2023, 11:51pm UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/38 "2023-07-21T23:51:42Z")

</div>

> [@mkitti](#):
>
> ```julia
> sizehint!(A, 5)
> push!(A, 1)
> 
> ```

Oh yeah we went over this as a more faithful but less used analog to Rust’s `with_capacity` in the original thread this was split from.

We also laid out that even after `sizehint!`, `push!` is quite a lot slower (comparing `@btime`, 57ish on my machine) than `setindex!`. I don’t recall if Mo8it benchmarked Rust’s analog of setindex for comparison (and I think it would need unsafe Rust for the uninitialized vector to start), but they did link Rust source code showing a vector checks if there’s more `capacity` to skip almost all the vector-lengthening work to just incrementing the semantic length and setindexing the last index. Squinting at the code that `_growend!` `ccall`s (as someone who doesn’t know C or Rust, C is way harder to read), I can’t make out a similar skip so I’m guessing that’s why there’s this discrepancy.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [July 22, 2023, 7:17am UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/39 "2023-07-22T07:17:28Z")

</div>

> [@mkitti](#):
>
> In the other posts, I agreed that _accessing_ an `undef` array that has not been initialized is undefined behavior.

I still don’t understand this at all. If `v = Vector{Int}(undef, 3)`, then accessing `v` will return an `Int`. Is that not defined enough?

Perhaps for vectors of other types it’s different, but not for `Int`.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [July 22, 2023, 7:35am UTC](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899/40 "2023-07-22T07:35:44Z")

</div>

More importantly, a _valid_ Int! All bitpatterns are well defined for this type after all. You’re not guaranteed any particular `Int` though. This is why I don’t want to call this undefined behavior.

[Previous page](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899.md?page=1)

[Next page](https://discourse.julialang.org/t/is-accessing-an-undef-array-undefined-behavior/101899.md?page=3)
