Protocol constraint Equatable in Swift with ReactiveSwift map function

128 Views Asked by At

I am using ReactiveSwift. There are two protocols: CommonProtocol and SpecificProtocol. When I use SpecificProtocol in map function there is an error Type 'any SpecificProtocol' cannot conform to 'Equatable'. However it is perfectly fine with using ConreateItem

protocol CommonProtocol {}

protocol SpecificProtocol: CommonProtocol, Equatable

extension SpecificProtocol where Self: Equatable {
    static func == (lhs: Self, rhs: Self) -> Bool { return true }
}

class ConreateItem: SpecificProtocol{}

var messages = MutableProperty<[CommonProtocol]?>(nil)

messages.producer
//  .map({ $0 as? [ConreateItem] }) //that is ok
    .map({ $0 as? [any SpecificProtocol] })//Type 'any SpecificProtocol' cannot conform to'Equatable'
    .skipRepeats()

I don't understand why/how SpecificProtocol is converted to any SpecificProtocol. And what is the correct way to implement Equatable constraint on protocol so I could use it in map function (Equatable is required for high order function in my case) map is of ReactiveSwift

1

There are 1 best solutions below

6
JeremyP On

tl;dr == in Equatable requires both parameters to be of the same concrete type. This cannot be determined if the compiler only knows that each of the two instances is any Equatable.

Without looking too closely at skipNil() it looks like it needs a sequence of objects that conform to Equatable. SpecificProtocol does not declare conformance to Equatable, it just declares a function == (which has a compiler error because it does not return a Bool), so there is a type error.

By the way, note that protocols are not types in Swift which is why the "any" is there. It's short for "anything whose type conforms to SpecificProtocol". At some point in the future, you'll have to write any explicitly in your declarations. e.g. [any SpecificProtocol]

To make SpecificProtocol instances conform to Equatable you need

protocol SpecificProtocol: CommonProtocol, Equatable
{
...
}

You can keep the extension to define a default == function or allow concrete types to define their own implementations of it.

Update

The reason you see the error message even with SpecificProtocol conforming to Equatable is because instances of arbitrary types that both conform to Equatable cannot be compared for equality. "foo" == 3 is not allowed even though String and Int are both equatable.

In order for the array that you return from map to be Equatable, it must be able to compare its elements against the elements of another array of the same type and if you know nothing about the elements except they both conform to SpecificProtocol you can't compare them for equality because Equatable conformance requires both parameters of == to be of the same concrete type.

Also, I don't think your map closure should be returning an array. I think it should look like .map({ $0 as? any SpecificProtocol })