1

I have a struct with a member named email of type String, and a protocol with a member named email of type String?. I would think it should be trivial to have the struct conform to that protocol, since it has the necessary information, but I'm being blocked by the compiler. Here's a pared down example I put together in a playground:

struct SimpleStruct {
    let email: String
}

protocol SimpleProtocol {
    var email: String? { get }
}

// Type 'SimpleStruct' does not conform to protocol 'SimpleProtocol'
extension SimpleStruct: SimpleProtocol {} 

The error specifically says:

Candidate has non-matching type 'String'

It also offers a fix:

Do you want to add protocol stubs?

But unsurprisingly that leads to a redeclaration error.

extension SimpleStruct: SimpleProtocol {
    var email: String? {
        // Invalid redeclaration of 'email'
    }
}

I understand that String and Optional<String> are different types, but I would expect that simply saying SimpleStruct conforms to SimpleProtocol with no additional code should compile, for the same reason that I can pass a string to a function that expects an optional string. Can someone explain why I'm mistaken?

Note: I realize I can get around this issue in a number of ways, most notably just renaming the email field in SimpleProtocol. But I'd like to understand why this is necessary as well as know if there's a more elegant solution.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Jamie A
  • 881
  • 1
  • 6
  • 14
  • answer to the same question https://stackoverflow.com/a/33113757/5397625 – Anton Rodzik Jul 23 '19 at 21:35
  • 1
    This is essentially the same limitation as in [Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?](https://stackoverflow.com/q/42561685/2976878), except instead of a get-only property requirement of protocol type being satisfied by a property of a conforming type, you're looking for a more general subtyping relationship. There's no technical reason why it's not possible, it just hasn't been implemented yet (though unfortunately an implementation now would come with source compatibility concerns). – Hamish Jul 23 '19 at 21:35
  • @Hamish thanks for linking to that question, I appreciate the detail you went into in your answer there! – Jamie A Jul 23 '19 at 22:56
  • I would say the surprise is the other way. The fact that given an Optional wrapping a String, it is legal to assign a String directly to it, is just weird. You’ve gotten used to it, but it is based on some bizarre mechanism baked into the language. After all you can’t assign a String where a `Wrapper` is expected. – matt Jul 24 '19 at 01:08

1 Answers1

0

Like you said, String and String? are not the same type, you simply can't conform to a protocol if your types don't match.

Picture what would happen if the protocol declared the variable like this { get set } and another object tried to assign a nil value. It would crash because your type is not actually honoring the protocol.

What you could do is to create another variable that's not optional and store the value there, and implement email like this:

var email: String? { return emailValue }

This way you have the non optional you want but still conform to the protocol. But doing exactly what you want is not possible.

EmilioPelaez
  • 18,758
  • 6
  • 46
  • 50