Protocol Extension Initializer Forcing to Call Self.Init

Protocol Extension Initializer forcing to call self.init

Consider this example:

protocol P {
init()
}

extension P {
init() {

} // error: 'self.init' isn't called on all paths before returning from initializer
}

struct S : P {
var str: String
}

let s = S()
print(s.str)

Suppose it compiled – we'd be able to create an S value without providing a value for the str property. That's why the compiler is complaining that your protocol extension implementation of init() isn't calling self.init. It needs you to chain to some other initialiser requirement – one that you don't provide a default implementation for (otherwise you could get into a recursive loop, as you found out), and therefore one that the adopting type needs to implement such that it can fully initialise itself.

For example, this is legal:

protocol P {
init()
init(str: String)
}

extension P {
init() {
self.init(str: "some default")
}
}

struct S : P {
var str: String
}

let s = S()
print(s.str) // some default

because now we're chaining to the init(str:) requirement, which S must implement (in this case it's satisfied by the implicit memberwise initialiser).

How to define initializers in a protocol extension?

As you can see this doesn't work under these circumstances because when compiling, one has to make sure that all properties are initialized before using the struct/enum/class.

You can make another initializer a requirement so the compiler knows that all properties are initialized:

protocol Car {
var wheels : Int { get set }
// make another initializer
// (which you probably don't want to provide a default implementation)
// a protocol requirement. Care about recursive initializer calls :)
init()
init(wheels: Int)

}

extension Car {
// now you can provide a default implementation
init(wheels: Int) {
self.init()
self.wheels = wheels
}
}

// example usage

// mark as final
final class HoverCar: Car {
var wheels = 0
init() {}
}

let drivableHoverCar = HoverCar(wheels: 4)
drivableHoverCar.wheels // 4

As of Xcode 7.3 beta 1 it works with structs as expected but not with classes since if they are not final the init(wheels: Int) in the protocol is a required init and it can be overridden therefore it cannot be added through an extension. Workaround (as the complier suggests): Make the class final.

Another workaround (in depth; without final class)

To work with classes without making them final you can also drop the init(wheels: Int) requirement in the protocol. It seems that it behaves no different than before but consider this code:

protocol Car {
var wheels : Int { get set }
init()
// there is no init(wheels: Int)
}

extension Car {
init(wheels: Int) {
self.init()
print("Extension")
self.wheels = wheels
}
}

class HoverCar: Car {
var wheels = 0
required init() {}
init(wheels: Int) {
print("HoverCar")
self.wheels = wheels
}
}

// prints "HoverCar"
let drivableHoverCar = HoverCar(wheels: 4)

func makeNewCarFromCar<T: Car>(car: T) -> T {
return T(wheels: car.wheels)
}

// prints "Extension"
makeNewCarFromCar(drivableHoverCar)

So if you make a Car from a generic context where the type on which you call init is only to be known as Car the extension initializer is called even though an initializer is defined in HoverCar. This only occurs because there is no init(wheels: Int) requirement in the protocol.

If you add it you have the former problem with declaring the class as final but now it prints two times "HoverCar". Either way the second problem probably never occurs so it might be a better solution.

Sidenote: If I have made some mistakes (code, language, grammar,...) you're welcome to correct me :)

Calling a protocol extension initializer

An init within a protocol is required, and therefore has to be implemented explicitly i.e. a default implementation cannot be utilised.

As for 'explicitly calling a protocol extension's initializer', you cannot instantiate a protocol type.

I would suggest using inheritance for this.

Why does Swift disallow assignment to self in class init, but not in protocol init?

It seems this currently behaves as expected.

The whole problem has been discussed on the swift forums: Assigning to Self in Protocol Extensions

The last time this quirk came up in internal discussions, the thought some of us had was that it might be worthwhile to prohibit classes from conforming to protocols with mutating requirements altogether. If you think about it, this makes some amount of sense — it seems like it would be quite hard to write code that can operate on both mutable values and mutable references generically, since the latter do not have value semantics:

var x = y
x.mutatingProtocolRequirement()
// did y change too?

However the discussion sort of fizzled out.

Protocol Extension Initializer

You have to provide a valid chain of init for creating an instance of a class and that limits your options for initializers in protocols.

Since your protocol can't be certain to cover all members of the class that uses it, any initializer you declare in your protocol will need to delegate initialization of the "unknown" members of the class to another initializer provided by the class itself.

I adjusted your example to illustrate this using a basic init() as the delegation initializer for the protocol.

As you can see, this requires that your class implement initial values for all members when init() is called. In this case I did that by providing default values in each member's declaration. And, since there isn't always an actual initial value for some members, I changed them to auto-unwrap optionals.

And to make thinks more interesting, your class cannot delegate initialization to a protocol supplied initializer unless it does so through a convenience initializer.

I wonder if all these restrictions are worth the trouble. I suspect you're trying to use a protocol because you need a bunch of common variables to be initialized consistently between classes that implement the protocol. Perhaps using a delegate class would provide a less convoluted solution than protocols (just a thought).

protocol Thing:AnyObject
{
var color:UIColor! { get set }
init()
}

extension Thing
{
init(color:UIColor)
{
self.init()
self.color = color
}
}

class NamedThing:Thing
{
var name:String! = nil
var color:UIColor! = nil

required init() {}

convenience init(name:String,color:UIColor)
{
self.init(color:color)
self.name = name
}
}

Swift protocol extension with specific Self type

Is there a way to specify a default value for the parameter for specific conforming structs when doing a protocol extension?

You have highlighted the problems with this approach in the question already.


How can I solve it in a different way?

UnsignedInteger inherits from BinaryInteger that can provide you bitWidth information (UInt8 => 8, UInt16 => 16 and so on).

extension UnsignedInteger {
func hex(uppercase: Bool = true) -> String {
let fieldWidth = self.bitWidth / 4
return String(format: "%0\(fieldWidth)\(uppercase ? "X" : "x")", self as! CVarArg)
}
}

Above makes it work for UInt, UInt8, UInt16, UInt32 & UInt64.


Taking it one step further, you can do this with FixedWidthInteger & now it will work for all signed and unsigned integers.
Sample Image

Is default implementation of a get-set variable possible in a protocol extension?

You can provide default implementations of get/set properties for computed values, but you can’t add storage to a type from an extension.



Related Topics



Leave a reply



Submit