Can anyone confirm if there is actually a runtime performance cost to using

let foo: [Int] = []

vs

let foo = [Int]()

As suggested here: https://github.com/nicklockwood/SwiftFormat/issues/1887#issuecomment-3209626590

I'd be surprised if the compiler doesn't just optimize this out somehow, but I don't actually have any basis for that assumption.

[New Rule]: A standard style for empty collection inits · Issue #1887 · nicklockwood/SwiftFormat

Barring other considerations, these two forms mean the same thing for Array and Set: let x: [Int] = [] let x = [Int]() let y: Set<Int> = [] let y = Set<Int>() I don't see a rule to allow preferring...

GitHub

@nicklockwood First is the correct one. If you need to give some default values instead it’s easy to change and aligns nicely with other initialization

Sorry no idea which one is faster though :-)

@nicklockwood have you checked godbolt?
@dlx nope. Good idea
@nicklockwood @dlx for these things I always struggle to come up with examples, because at maximum optimization, the compiler could inline the contents of the array if not empty, too :/

@nicklockwood https://godbolt.org/z/Tsvb8Eh9a

With optimizations on, it _should_ not matter.

Compiler Explorer - Swift (x86-64 swiftc 6.1)

let x: [Int] = [] let y = [Int]()

@nicklockwood I assumed the compiler would treat both the same. I remember there being some stuff way back at the start of switch talking about how type notation was better for compiler speed but that was about it.
Swift Regret: Inferred Property Types

Type inference makes sense for local variables, but not properties or globals.

-dealloc

@nicklockwood @beno For the type-checking: [Int]() is a pretty simple expression, but the compiler does still have to check if it’s a failable initializer, so from another file the Regret still applies (but honestly you will probably barely notice it).

At run time: for Array specifically the compiler will probably special-case the empty literal as an optimization (I haven’t checked recently), but abstractly that’s going through the general “make an Array with these items” path, which may or may not have a special case for 0 items (I believe it does). Whereas init() knows it has zero items and can take shortcuts (if there are any to be taken). Again, though, this really is going to be a micro-optimization, even less important than reserving capacity if you know you’re going to push several items into an array one at a time. And how often do people bother with that?

@nicklockwood is this a typo, or is there a way to populate an array declared with a “let” keyword?

@a40yostudent it's not a typo, but you are correct there would be no way to add items to the array after declaring it like this.

I could have used var instead - it's not relevant to my question (at least, I don't think it is).

@nicklockwood @a40yostudent Not sure if Swift does this, but in principle, the compiler could probably replace the let-bound array with a constant empty array (and not allocate anything)
@nicklockwood My assumption was that the compiler would have treated both cases identically but a quick test showed different assembly codes for each option.
@joaop yep, that surprises me too. Although I'm not fluent enough in assembly to tell the significance of the differences.

@nicklockwood The main differences there are:
1. let x: [Int] = [] has one additional instruction loading 0 into a register (line 14 on the right)
2. They branch into different initialization routines (line 16 on the left or 17 on the right)

That doesn't necessarily mean it's less efficient though, I didn't inspect the different routines and it's possible the one called by let x = [Int]() will have that additional load internally.

@nicklockwood based on this post, it seems that empty Swift arrays (and dictionaries) are singletons and don’t allocate memory, so shouldn’t cause performance impact https://forums.swift.org/t/short-array-optimisation/68082/2
Short Array optimisation

Empty arrays, dictionaries, and sets use singleton storage objects (example), so they don't actually allocate memory. It is marked an immortal object, so reference-counting operations should be extremely cheap for it. As for small arrays, I guess it just wasn't worth it. String is twice the size of Array (16 bytes vs 8 bytes). I remember there being some debate about this, but it was considered very important and desirable that Array not be any larger than a single pointer (I think the reason m...

Swift Forums
@rileytestut makes sense, although this init pattern is also used for non-empty arrays.
@nicklockwood what’s the alternative in that case though? Allocating an empty array with Array() then appending each element individually?

@nicklockwood There is no difference whatsoever. (It would be a pretty serious stdlib bug if there was any; so please do report if you find otherwise.)

It’s a matter of taste, but I expect most developers find initialization using the empty literal to be more readable — that is why this notation exists. It would be quite a shame if tools discouraged its use.