Why Can't a Get-Only Property Requirement in a Protocol Be Satisfied by a Property Which Conforms

Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?

There's no real reason why this shouldn't be possible, a read-only property requirement can be covariant, as returning a ConformsToB instance from a property typed as ProtocolB is perfectly legal.

Swift just currently doesn't support it. In order to do so, the compiler would have to generate a thunk between the protocol witness table and conforming implementation in order to perform the necessary type-conversion(s). For example, a ConformsToB instance would need to be boxed in an existential container in order to be typed as ProtocolB (and there's no way the caller can do this, as it might not know anything about the implementation being called).

But again, there's no reason why the compiler shouldn't be able to do this. There are multiple bug reports open over this, this one which is specific to read-only property requirements, and this general one, in which Slava Pestov, a member of the Swift team, says:

[...] we want protocol witnesses and method overrides in every case where a function conversion is allowed

So it definitely looks like something the Swift team are looking to implement in a future version of the language.

In the mean time however, as @BallpointBen says, one workaround is to use an associatedtype:

protocol ProtocolA {
// allow the conforming type to satisfy this with a concrete type
// that conforms to ProtocolB.
associatedtype SomeProperty : ProtocolB
var someProperty: SomeProperty { get }
}

protocol ProtocolB {}
class ConformsToB: ProtocolB {}

class SomeClass: ProtocolA {

// implicitly satisfy the associatedtype with ConformsToB.
var someProperty: ConformsToB

init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
}

But this is quite unsatisfactory, as it means that ProtocolA is no longer usable as a type (because it has associatedtype requirements). It also changes what the protocol says. Originally it said that someProperty could return anything that conformed to ProtocolB – now it says that an implementation of someProperty deals with just one specific concrete type that conforms to ProtocolB.

Another workaround is just to define a dummy property in order to satisfy the protocol requirement:

protocol ProtocolA {
var someProperty: ProtocolB { get }
}

protocol ProtocolB {}
class ConformsToB: ProtocolB {}

class SomeClass: ProtocolA {

// dummy property to satisfy protocol conformance.
var someProperty: ProtocolB {
return actualSomeProperty
}

// the *actual* implementation of someProperty.
var actualSomeProperty: ConformsToB

init(someProperty: ConformsToB) {
self.actualSomeProperty = someProperty
}
}

Here we're essentially writing the thunk for the compiler – but it's also not particularly nice as it adds a needless property to the API.

Need to satisfy Swift protocol requirement by using a specific subclass of the requirement (or type that conforms to it)

To conform to protocol A, Model2 would need a member var a that allows storing a reference to anything conforming to protocol A, not just a reference to a Model1. So you can't do this.

Why sub-protocol doesn't fulfill conformance for its parent?

B describes a type that conforms to some set of rules. It is not itself a type that conforms to those rules. B does not conform to B, let alone anything it requires additional conformance to. protocol B:A says "anything that conforms to B also must conform to A." However, B does not conform to B, and so does not conform to A.

This has been answered many times on SO, but to repeat the "why," it comes down most simply to this example:

protocol A { 
init()
}

func f(type: A.Type) {
let a = type.init()
}

f(type: A.self)

If A itself conforms to A, then this should be legal. But what init should f call? In the presence of init and static requirements, it's not possible for protocols to conform to themselves.

While the specific covariance you want in this case is possible in principle, Swift doesn't have the ability to distinguish between legal and illegal covariance, and disallows all of it. For example, consider the small variation from immutable to mutable:

protocol C {
var property: A { get set }
}

In this case, it is definitely impossible for Bar to conform (it would have to provide a setter for property that accepted any A, even though the getter would return a subtype of A). Swift is not able to distinguish all the cases where it is possible (generally when everything is immutable and there are no init or static requirements) from the cases where it isn't. There's been some discussion of allowing these kinds of cases where it's technically possible, but the concern is that very small changes to the protocol could then break your entire type structure, sometimes for non-obvious reasons. Instead, at least for now, type requirements are generally invariant.


A typical way to address this overall issue is to just provide accessors for the types you want. For example:

protocol A { }
protocol B: A { }

protocol C {
var aProperty: A { get }
}

struct Foo: C {
let aProperty: A
}

struct Bar: C {
var aProperty: A { return bProperty }
let bProperty: B
}

This provides greater flexibility (you can make bProperty a var if you like), and makes your code more explicit.

Why am I allowed to set a read only property of a protocol using a struct that inherits said protocol?

What am I doing wrong to make it a true read only property to where I can't set it?

There is a difference between a protocol (a set of rules) and the type (i.e. your struct) that adopts the protocol.

  • Your protocol rule says that readOnlyProperty should be readable.

  • Your struct obeys by making it readable, and also makes it writable. That is not illegal, so all is well — and readOnlyProperty in your struct is read-write.

What would have been illegal would be the inverse, i.e. for the protocol to declare a property read-write but the adopter to declare it read-only. That situation didn't arise in your example, but if it had, the compiler would have stopped you.

Swift Protocol Optional conformance via Non-Optional

If the protocol provides a default implementation that returns an optional:

protocol SomeProtocol {
var foo: Int? { get }
}

extension SomeProtocol {
var foo: Int? { return nil }
}

protocol-conforming types can then provide an overriding non-optional version of the variable/function:

struct StructB: SomeProtocol {
let foo: Int
}

I found this discussed on the Swift Evolution forum:

At the first glance I thought there is a rule that allows us to satisfy protocol requirements with non-optional types, but this resulted in an error. Only after further investigation I noticed that a default implementation must exist in order to 'kind of override' the requirement with a non-optional version.

https://forums.swift.org/t/how-does-this-rule-work-in-regard-of-ambiguity/19448

This Swift team also discusses allowing non-optional types to satisfy optional-value protocols:

Would it make any sense to allow protocol requirement satisfaction with non-optional types, like with failable init's? (Probably with some implicit optional promotion.)

Yep, totally! Except for the part where this changes the behavior of existing code, so we'd have to be very careful about it. This is considered part of [SR-522] Protocol funcs cannot have covariant returns

which is tracked on Stack Overflow here:

Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?

Swift protocol conformance issue

There's no fundamental reason why Swift can't support this, but it just doesn't (as of now). This answer does a good job explaining.

The best solution I think, while unfortunately ugly, is to make two properties.

class SomeClass: HelperProtocol {
weak var strictlyTypedDelegate: (SomeProtocol & AnotherProtocol)?
var delegate: AnotherProtocol? { strictlyTypedDelegate }
}


Related Topics



Leave a reply



Submit