[swift-users] A confusing protocol extension behaviour/bug

Howard Lovatt howard.lovatt at gmail.com
Mon Jan 1 22:27:33 CST 2018


Rest of message - hit send by mistake. 

The problem is that it is really easy to make the mistake you made. There have been a number of suggestions on Swift Evolution to add key words so that diagnostics can be improved. For example:

    protocol P {
        func m()
    }
    extension P {
        override func m() { ... }
    }

Which obviously breaks old code but does let the compiler diagnose problems. However none of these suggestions have gained enough support to date. 

You could put in an RFE asking for better diagnostics. 

-- Howard. 

> On 1 Jan 2018, at 11:21 pm, Howard Lovatt via swift-users <swift-users at swift.org> wrote:
> 
> Unfortunately the error messages you get with protocols are limited :). The compiler is doing the right thing, no bug. The problem is that it is really easy to mak
> 
> -- Howard. 
> 
>> On 1 Jan 2018, at 3:40 pm, Toni Suter via swift-users <swift-users at swift.org> wrote:
>> 
>> Hi Marc,
>> 
>> There are several subtleties here, but I think the compiler is actually doing the right thing.
>> 
>> The second class defines a static property that looks like it is 'overriding' the static property from
>> the protocol extension, but since the types don't match (String vs. String?), it sort of 'overloads'
>> the property (similar to function overloading). Nevertheless, the class still fulfills the requirements
>> of the Trackable protocol, by inheriting the static property from the protocol extension.
>> 
>> When you access analyticsID like a regular static property, the Swift compiler will choose the String property,
>> because it shadows the String? property:
>> 
>> let x = Something2.analyticsID
>> print(x)					// Wrong but compilers, returns wrong value
>> print(type(of: x))				// String
>> 
>> However, when the context of the expression Something2.analyticsID expects a String?, the Swift compiler will
>> choose the String? property:
>> 
>> let a: String? = Something2.analyticsID		// explicit type annotation demands a String?
>> print(a)					// nil
>> print(type(of: a))				// Optional<String>
>> 
>> let b = Something2.analyticsID as String?	// type cast demands a String?
>> print(b)					// nil
>> print(type(of: b))				// Optional<String>
>> 
>> A similar thing happens, when you write Something2.analyticsID ?? "nil". The nil coalescing operator ?? demands that the first parameter
>> is an optional. Therefore, the Swift compiler will choose the String? property instead of the String property.
>> 
>> I hope this helps!
>> 
>> Best regards,
>> Toni
>> 
>>> Am 01.01.2018 um 18:29 schrieb Marc Palmer via swift-users <swift-users at swift.org>:
>>> 
>>> Hi,
>>> 
>>> I hope everybody had a great New Year celebration.
>>> 
>>> I was tracking down a weird bug in my Swift code today. A property defined in a class in order to conform to a protocol was not being seen. A protocol extension provided a default value of `nil` for this property, so I knew where it was coming from. Turned out, in my class I had defined the property with the correct name but incorrect type - I declared it as `String` instead of `String?`. 
>>> 
>>> I isolated this behaviour in a playground, shown below, and it is pretty weird behaviour.
>>> 
>>> The output is:
>>> 
>>> Something1 has id: nil
>>> Something2 has id: nil
>>> Something3 has id: Correct
>>> -- Direct access--
>>> Something1 - nil
>>> Something2 - nil
>>> Something2 with String(describing:) - Wrong but compiles, returns wrong value
>>> Something3 - Correct
>>> 
>>> The playground code:
>>> 
>>> ======================
>>> 
>>> protocol Trackable {
>>>    static var analyticsID: String? { get }
>>> }
>>> 
>>> extension Trackable {
>>>    static var analyticsID: String? { return nil }
>>> }
>>> 
>>> class Something1: Trackable {
>>> }
>>> 
>>> class Something2: Trackable {
>>>    static var analyticsID: String = "Wrong but compiles, returns wrong value"
>>> }
>>> 
>>> class Something3: Trackable {
>>>    static var analyticsID: String? = "Correct"
>>> }
>>> 
>>> func getID<T: Trackable>(_ trackable: T.Type) {
>>>    if let id = trackable.analyticsID {
>>>        print("\(trackable) has id: \(id)")
>>>    } else {
>>>        print("\(trackable) has id: nil")
>>>    }
>>> }
>>> 
>>> getID(Something1.self)
>>> getID(Something2.self)
>>> getID(Something3.self)
>>> 
>>> print("-- Direct access--")
>>> print("Something1 - \(Something1.self.analyticsID ?? "nil")")
>>> print("Something2 A - \(Something2.self.analyticsID ?? "nil")")
>>> print("Something2 with String(describing:) - \(String(describing: Something2.self.analyticsID))")
>>> print("Something3 - \(Something3.self.analyticsID ?? "nil")”)
>>> ======================
>>> 
>>> Thanks in advance for any information about my misinterpretations or recommendations of what parts are actually undesirable so that I can raise the JIRAs.
>>> 
>>> Cheers
>>> 
>>>>>> Marc Palmer
>>> Montana Floss Co. Ltd.
>>> 
>>> Soundproof – Music Player for Practice 
>>> http://getsoundproof.com
>>> 
>>> 
>>> 
>>> _______________________________________________
>>> swift-users mailing list
>>> swift-users at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-users
>> 
>> _______________________________________________
>> swift-users mailing list
>> swift-users at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-users
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20180101/47ae4382/attachment.html>


More information about the swift-users mailing list