Usage before declaration

I was playing with some examples in TSPL, and was surprised to find out that the following snippet

class Person {
    var residence: Residence?
}

class Residence {
    var rooms: [Room] = []
    var numberOfRooms: Int {
        return rooms.count
    }

    subscript(i: Int) -> Room {
        get {
            return rooms[i]
        }
        set {
            rooms[i] = newValue
        }
    }

    func printNumberOfRooms() {
        print("The number of rooms is \(numberOfRooms)")
    }

    var address: Address?
}

class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    
    func buildingIdentifier() -> String? {
        return if let buildingNumber, let street {
            "\(buildingNumber) \(street)"
        } else if let buildingName {
            buildingName
        } else {
            nil
        }
    }
}

class Room {
    let name: String
    init(name: String) { self.name = name }
}

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}

let john = Person()

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}

both compiles and results in runtime crash.
Is it a bug or a feature?

I suspect that it compiles because john is a global value - but why is it unwrapped as Int! instead of Int?

There are some long-standing bugs with uninitialized access to top-level variables in script mode (what you get if you run swiftc with a single file that has top-level code in it, or if your module has a main.swift). It should be fixed certainly, but you'll find that library globals, local variables, and stored properties of types don't have this problem.

3 Likes