Skip to content

Swift type inference on default value

Published: at 09:36 AM

Type inference is a feature in Swift where the compiler can deduce a type from a variable based on the value it’s assigned. While it helps to enhances code readibility and verbosity, some blog and forum post report that it can impact in compile time of a codebase.

Lucan Van Dongen has an excellent post breaking down that different type of initializer result in different type checking result. He also show that creating large nested struct with .init() can slow compile time up 30 times slower.

But how is the type-checking impact on constructing type for default value? This got me curious upon doing some code review today. So let’s dive in.

Take this code as example:

let trackerName: String? = nil

// default value using literal string
let formatted = trackerName ?? ""
let formatted = trackerName ?? String()

While both of the default value return empty string, turns out the second one with String() is slower to type check.

To proof it, I create a simple python script that create a swift file, then run benchmark using hyperfine.

Here’s the script:

#!/usr/bin/env python3
import os

filenames = [
 { 'filename': "a-nil", 'name': "String literal"},
 { 'filename': "b-nil", 'name': "String empty init"},
 { 'filename': "c-nil", 'name': "Int literal"},
 { 'filename': "d-nil", 'name': "Int empty init"},
]

code = [
  'let a{} = x ?? ""',
  'let b{} = x ?? String()',
  'let c{} = x ?? 0',
  'let b{} = x ?? Int()',
]

for (i, filename) in enumerate(filenames):
    file = filename['filename']

    with open(file + ".swift", "w") as f:
        if file == 'a-nil' or file == 'b-nil':
            s = "let x: String? = nil\n"
        else:
            s = "let x: Int? = nil\n"

        for j in range(1000):
            s += (code[i] + '\n').format(j)
        f.write(s)
    os.system("hyperfine 'xcrun swiftc -typecheck {0} using {1}'".format(file + ".swift", filenames[i]['name']))

Based on the hyperfine result, literal default value is faster to check rather than calling String() or Int()

String literalString() initInt literalInt() init
430.9596.9399.9584.3

This measurements are in ms and from the result we can see that using literal for default value is 40% faster to type check than calling the init for its type.

"swift type inference.png"

While the difference of both method takes around 150ms, this can impact on compile time of large codebase. Especially if there’s lots of usage String() or Int() inside.