Search code examples
swiftinitializationprotocolsswift-extensions

Redundent extension for protocol conformance in Swift


protocol EmptyInitializable {
    init()
}

@propertyWrapper
struct PropertyWrapper: EmptyInitializable {
    let wrappedValue: Int
    
    init(_ wrappedValue: Int = 0) {
        self.wrappedValue = wrappedValue
    }
}

// This one lools redundent
extension PropertyWrapper {
    public init() {
        wrappedValue = 0
    }
}

Why do we need that extansion at the bottom? Why can't compiler just figure out, that struct PropertyWrapper already complies with the requirements for protocol EmptyInitializable? It looks so ugly...

P.S. I beleive that it actually used to work in the similar situations before.


Solution

  • The compiler does not lower

    init(_ wrappedValue: Int = 0) {
        self.wrappedValue = wrappedValue
    }
    

    to

    init(_ wrappedValue: Int) {
        self.wrappedValue = wrappedValue
    }
    
    init() {
        self.init(0)
    }
    

    Because otherwise the number of overloads the compiler has to generate grows exponentially for each optional parameter, among other reasons. For a method with 5 optional parameters, the compiler would need to generate 32 different signatures.

    With that said, PropertyWrapper clearly does not conform to EmptyInitializable.

    What actually happens is that there is only one overload, but for each optional parameter, a thunk is generated to compute the value of that parameter.

    init(_ wrappedValue: Int) {
        self.wrappedValue = wrappedValue
    }
    
    func defaultValueForArgument0OfInit() {
        return 0
    }
    

    There is also a bit of metadata that tells the compiler to generate calls to that thunk, when the caller has not provided all arguments. The call PropertyWrapper() is lowered to PropertyWrapper(defaultValueForArgument0OfInit()). You can see the compiled code in godbolt.org.

    If you are calling init through a protocol, the call is dynamically dispatched, so compiler doesn't even know which implementation will be called, let alone whether it needs to pass the default values of optional parameters.

    Of course, the compiler could be designed in other ways that make this possible, but asking why it hasn't been designed in those ways, isn't really answerable, unless you are being very specific.

    Side note: since you created a protocol just for the types that can be initialised without parameters, I recommend reading Protocols are more than Bags of Syntax.