What is the difference in defining the associated types in a protocol directly that it inherits from another protocol vs using generic type constraints? For example B I get this error in TestB:
Member 'read' cannot be used on value of type 'any RepositoryB'; consider using a generic constraint instead
Could somebody explain the difference? From my understanding this should be the same as it also expects the same implementation from entities conforming to it (see RepositoryAImpl and RepositoryBImpl).
public protocol CRUDRepository {
associatedtype Item
associatedtype ReadInput
func create(_ item: Item) async throws
func read(_ input: ReadInput) -> AsyncThrowingStream<[Item], Error>
func update(_ item: Item) async throws
func delete(_ item: Item) async throws
}
public protocol RepositoryA: CRUDRepository where Item == String, ReadInput == Query {}
public protocol RepositoryB: CRUDRepository {
associatedtype Item = String
associatedtype ReadInput = Query
}
public struct Query {}
struct RepositoryAImpl: RepositoryA {
func create(_ item: String) async throws {
}
func read(_ input: Query) -> AsyncThrowingStream<[String], Error> {
AsyncThrowingStream { continuation in
continuation.yield(["Test"])
}
}
func update(_ item: String) async throws {
}
func delete(_ item: String) async throws {
}
}
struct RepositoryBImpl: RepositoryB {
func create(_ item: String) async throws {
}
func read(_ input: Query) -> AsyncThrowingStream<[String], Error> {
AsyncThrowingStream { continuation in
continuation.yield(["Test"])
}
}
func update(_ item: String) async throws {
}
func delete(_ item: String) async throws {
}
}
struct TestA {
private let repository: any RepositoryA
init(repository: any RepositoryA) {
self.repository = repository
}
func start() -> AsyncThrowingStream<[String], Error> {
repository.read(Query())
}
}
struct TestB {
private let repository: any RepositoryB
init(repository: any RepositoryB) {
self.repository = repository
}
func start() -> AsyncThrowingStream<[String], Error> {
repository.read(Query())
}
}
The problem here is that
associatedtype Item = Stringdoesn't enforce comforming type'sConformingType.Item == String. It merely gives a default type forConformingType.Item, and conforming type is still allowed to override it throughtypealias Item = ....If you don't want to use same-type constraint via a where clause, you can use
typealiasinstead: