Search code examples
swiftgenericsprotocols

Swift Generics: Conforming to protocol by constraining generic parameters to variable types defined by said protocol


In my Domain module I am defining a protocol Assignment, TimeLog and AssignmentTimeLog.

When defining a concrete implementation I want to use generics to conform to the AssignmentTimeLog protocol. To do so I am constraining my generic A, T parameters to be of type Domain.Assignment and Domain.TimeLog. Why does this not satisfy the protocol requirements? I want to understand the logic of what's going on.

// Domain Module
public protocol AssignmentTimeLog {

    var assignment: Assignment { get }
    var timeLog: TimeLog { get }
}

// My attempt to create an implementation trows an error
// Error: 
// Type 'AssignmentTimeLog<A, T>' does not conform to protocol 'AssignmentTimeLog'

import Domain

struct AssignmentTimeLog<A, T>: 
    Domain.AssignmentTimeLog where A: Domain.Assignment, T: Domain.TimeLog {
    
    var assignment: A
    var timeLog: T
 
}

For Context: The reason for using generics is that later I want to define an extension on AssignmentTimeLog where A & T also implement another protocol. This provides additional functionality without additional code. Concrete types implement the Domain protocols as well as those additional protocols.

I have tried to figure out this by reading the documentation and multiple blogs. But I can't seem to zone in on the exact issue/gap in understanding that I have.


Solution

  • The protocol says a different thing from what your implementation says.

    Fact 1

    According to the protocol this getter

    var assignment: Assignment { get }
    

    can return any value conforming to Assignment.

    Fact 2

    On the other hand your implementation here

    var assignment: A
    
    

    says that assignment will contain a value of a specific type A (which happens to conform to Assignment).

    These are 2 very different statements.

    The fix

    Here's an easy fix

    protocol AssignmentTimeLog {
        
        associatedtype A: Assignment
        associatedtype B: TimeLog
        
        var assignment: A { get }
        var timeLog: B { get }
    }
    
    
    struct MyAssignmentTimeLog<A, T>: AssignmentTimeLog where A: Assignment, T: TimeLog {
        
        var assignment: A
        var timeLog: T
     
    }