Initializing an empty Dictionary - style question

Hi, I was reading some Wenderlich tutorials and I noticed people like to initialize Dictionaries like so:

var skipTable: [Character: Int] = [:]

But I think it's minutely easier to read:

var skipTable = [Character:Int]()

Is this just a style preference or is there a performance penalty here? Thanks.

For all intents and purposes, they're equivalent and simply a matter of style: Compiler Explorer

I will point out though that there's also the alternative

var skipTable = Dictionary<Character, Int>()

:wink:

2 Likes

i really don’t think either is superior but my personal style is to use

let dict:[T: T] = [:]

so that the type information lives on the left side of the assignment, just like any other Swift variable declaration. that’s also why i like to use

let buffer:UnsafeMutablePointer<Int> = .allocate(capacity: 13)

and

let foo:Foo = .init()
3 Likes

My reason for preferring var skipTable: [Character: Int] = [:] over the alternatives has to do with education and the order in which topics are introduced to programming students.

I am very strict about not using syntax or features a student hasn't learned yet, and not introducing things too early. As such, I teach data structures fairly early on, after control flow and functions, but before custom types. The syntax above lets me do that, as it uses a literal instead of an initializer for initialization (unlike var skipTable = [Character:Int]()) and does not require generics (unlike var skipTable = Dictionary<Character, Int>()).

3 Likes

Does anyone know why Swift requires a colon in empty dictionary literals?

The distinction from an empty array obviously make senses in a dynamically typed language. However, [] omits the type of its elements, and [:] omits the types of the keys and values, so (IIUC) you could only use [] or [:] in contexts where the types are already known, so [] could just express an empty array or dictionary of some inferred type.

It's not particularly important. I just wondered about it.

There's no good reason for that, it's just one of those early days decisions that's hard to undo now. If to find some excuses: to distinguish which function to call:

func foo(_ x: [String : String]) {
    print("dictionary version called")
}

func foo(_ x: [String]) {
    print("array version called")
}

func foo(_ x: Set<String>) {
    print("set version called")
}

foo([:])

without putting type explicitly at the call site:

foo([] as [String: String]) // not current Swift

Interestingly foo([]) call is unambiguous here (and it feels it should be ambiguous).

With [] for empty directories and along with making comma separators optional we could have supported conditional literals like so:

// not current Swift ahead

var foo: [String: String] = [
    #if os(macOS)
    "key" : "value"   // comma is optional here
    #endif
]

var bar: [String: String] = [
    "hello" : "world" // comma is optional here
    #if os(macOS)
    "key" : "value"   // comma is optional here
    #endif
]

PS. it's better starting new threads instead of resurrecting old threads.

3 Likes

@tera - I hadn't thought about the case you mentioned (foo([]) vs foo([:])).

PS. it's better starting new threads instead of resurrecting old threads.

I'll keep that in mind. Thanks.