I have been playing around with generics in Swift5.7 and ran into an error where I can't figure out the reason behind it.
The same protocol (Protocol2 in the example below) can be conformed to by a class but not by a struct.
I might be missing something very basic here. Happy for any suggestions or insights!
import Combine
protocol AnimalType: Hashable {
var name: String { get }
}
struct Chicken: AnimalType {
let name: String
}
// Protocols with primary associated types and "simple" function
protocol Protocol1<Animal> {
associatedtype Animal: AnimalType
func test(set: Set<Animal>)
}
// A struct conforming to this protocol without issues
struct TestStruct1: Protocol1 {
func test(set: Set<Chicken>) {
Set<Chicken>()
}
}
// A second protocol with a nested(?) generic combine publisher
protocol Protocol2<Animal> {
associatedtype Animal: AnimalType
var publisher: AnyPublisher<Set<Animal>, Never> { get }
}
// A struct that's not able to conform to this protocol
// * "Type 'TestStruct' does not conform to protocol 'Protocol2'"
// * Xcode's auto-fix is asking for a typealias for the generic type `typealias Animal = type`
struct TestStruct2: Protocol2 {
private(set) lazy var publisher = subject.eraseToAnyPublisher()
private var subject = PassthroughSubject<Set<Chicken>, Never>()
}
// Same implementation as above, expect that here we're using a class and it's working without any issues
class TestClass2: Protocol2 {
private(set) lazy var publisher = subject.eraseToAnyPublisher()
private var subject = PassthroughSubject<Set<Chicken>, Never>()
}
I suspect it is caused by the lazy type in struct.
If we change to use a normal getter or just get ride of the lazy type, it will work normally.
// A struct that's not able to conform to this protocol
// * "Type 'TestStruct3' does not conform to protocol 'Protocol2'"
// * Xcode's auto-fix is asking for a typealias for the generic type `typealias Animal = type`
struct TestStruct3: Protocol2 {
// At first, I thought it has something to do with the wrong type inference
// Actually it was the same as private(set) lazy var publisher = subject.eraseToAnyPublisher()
// You can confirm it by right click pulisher to get the type inference
private(set) lazy var publisher: AnyPublisher<Set<Chicken>, Never> = subject.eraseToAnyPublisher()
private var subject = PassthroughSubject<Set<Chicken>, Never>()
}
// Almost the same implementation, it's working without any issues
struct TestStruct4: Protocol2 {
var publisher: AnyPublisher<Set<Chicken>, Never> {
subject.eraseToAnyPublisher()
}
private var subject = PassthroughSubject<Set<Chicken>, Never>()
}
// Almost the same implementation, it's working without any issues
struct TestStruct5: Protocol2 {
private(set) var publisher = PassthroughSubject<Set<Chicken>, Never>().eraseToAnyPublisher()
}
Thank you so much for your input @Kyle-Ye and @cukr !
I understand the issue now. struct being an immutable value type and lazy mutating it after initiation. Thank you for the suggestions of mutating get and simply removing the lazy implementation.