Why the swift compiler behaves this way?

have code that resembles this

I created a custom subscript to do the unwrapping for me to make things easier.
I know that there is a safer way to do this by creating a safe subscript that returns a default value instead of force unwrapping

enum MyEnum {
    case one
    case two
    case three
}

struct MyStruct {
    static var empty: Self {
        return .init()
    }

    //Some Variables Here
}

class MyClass {
    var index = 0

    var data: [MyEnum: MyStruct] = [
        .two: .empty,
        .three: .empty,
        .one: .empty
    ]

    var allTypes: [MyEnum] {
        switch Bool.random() {
            case true:
                return [.two, .three]
            case false:
                return [.one]
        }
    }

    var currentSelectedType: MyEnum {
        return allTypes[index]
    }

    func myMethod(type: MyEnum) {
        let one: MyStruct = data[type]
        let two: MyStruct = data[currentSelectedType]
        let three: MyStruct = data[allTypes[index]]
        let four: MyStruct = data[.one]

    }
}

private extension Dictionary {
    subscript(_ key: Key) -> Value where Key == MyEnum, Value == MyStruct {
        get {
            return self[key]!
        }
        set {
            self[key] = newValue
        }
    }
}

Why the compiler does not recognize my subscript in the first two lines in myMethod?

Subscripting a dictionary returns an optional.

It is a common and intended use-case to call the subscript with a key that the dictionary may not contain.

Your custom dictionary subscript is completely unnecessary. Just use the force-unwrap operator !:

let one: MyStruct = data[type]!

The actual question seems to be, “Of the two overloads, why does the compiler select the one that is optional when the other one satisfies the type required by the expression?”

Something seems broken in the compiler, as different call sites are inconsistent about whether or not it works. Here is a minimal example:

extension Dictionary where Key: ExpressibleByStringLiteral, Value == Bool {
  subscript(index: Key) -> Value {
    return true
  }
  func use() {
    let _: Bool = self[""] // ✓ Just fine.
  }
}
func use() {
  let dictionary: [String: Bool] = [:]
  let _: Bool = dictionary[""] // ✗ Value of optional type 'Bool?' must be unwrapped to a value of type 'Bool'
}

Even more oddly, switching out where Key: ExpressibleByStringLiteral for where Key == String causes both to fail.

Please file a bug: bugs.swift.org

4 Likes