Why when I extend the ExpressibleByArrayLiteral protocol in swift I need to make the init required. In the definition of the protocol, the init method is just public.
I pretty much have the same thing as in the doc, https://developer.apple.com/reference/swift/expressiblebyarrayliteral, and still, the compiler complains about making this init(arrayLiteral: Element...)
required. The only difference I have is that I am implementing it in a class no a struct. Any suggestions?
UPDATE:
Here is an implementation of my code:
public class Stack<T> {
private var buffer: [T]
init() {
self.buffer = []
}
public func push(_ value: T) {
self.buffer.append(value)
}
public func pop() -> T? {
return self.buffer.popLast()
}
var size: Int {
return self.buffer.count
}
var isEmpty: Bool {
return self.size == 0
}
}
extension Stack: ExpressibleByArrayLiteral {
init(arrayLiteral: T...) {
for item in arrayLiteral {
self.push(item)
}
}
}
The errors I am getting are:
designated initializer cannot be declared in an extension of 'Stack'; did you mean this to be a convenience initializer?
initializer 'init(arrayLiteral:)' must be declared public because it matches a requirement in public protocol 'ExpressibleByArrayLiteral'
initializer requirement 'init(arrayLiteral:)' can only be satisfied by a
required
initializer in the definition of non-final class 'Stack'
Questions:
Error #1 -> Why can't I declare a designated init in an extension?
Error #2 -> I understand this part, the protocol defines this as public. But why does the docs implement it with out the public keyword?
Error #3 -> This is pretty much my biggest question, why this needs to be required.
The reason is inheritance: any init
inside a protocol must be marked as required
if adopted by a class. A struct
cannot be inherited.
For a class, An init
must initialize an instance of that same class or return nil
. When that class adopts a protocol, itself and all its subclasses must provide their own implementations so the compiler can verify that fact:
class ClassA : ExpressibleByArrayLiteral {
required init(arrayLiteral elements: Self.Element...) {
// this implicitly returns an instance of ClassA
}
}
class ClassB : ClassA {
// without its own init(arrayLitteral:), it will fallback on ClassA's init and return
// an instance of ClassA, which Swift does not allow.
}
This is due to Swift's static typing system. Unlike in Objective-C where you think you create an NSString
but actually get an _NSContiguousString
instead (the so-called class cluster design pattern)
NSString * str = [[NSString alloc] initWithString:@"Hello world"];
// surprise: you may get an _NSContiguousString instead
Just get rid of the extension and implement it in the class's definition:
public class Stack<T> : ExpressibleByArrayLiteral {
private var buffer: [T]
init() {
self.buffer = []
}
public func push(_ value: T) {
self.buffer.append(value)
}
public func pop() -> T? {
return self.buffer.popLast()
}
var size: Int {
return self.buffer.count
}
var isEmpty: Bool {
return self.size == 0
}
// MARK: -
required public init(arrayLiteral: T...) {
self.buffer = []
for item in arrayLiteral {
self.push(item)
}
}
}