Reinhard Männer
Reinhard Männer

Reputation: 15257

Cannot convert value of generic associated type of protocol to expected argument type

To learn Swift generics, I wrote a function that creates a tableView data source, i.e. a 2-dim (sections, rows) array of elements. The element type should be generic, and the created data source should be initialised with unique values of the elements.

I declared a protocol that is adopted by the possible element types:

protocol UniqueInit {
    associatedtype T
    static func uniqueInit() -> T
}

and the dataSource function.
Here, nrRowsInSection is a variadic parameter: The number of arguments given defines the number of sections, and the values of the arguments define the number of rows in the respective section.

static func dataSource<T: UniqueInit>(nrRowsInSection: Int...) -> [[T]] {
    var result: [[T]] = []
    for nrRows in nrRowsInSection {
        var row: [T] = []
        for _ in 0 ..< nrRows {
            row.append(T.uniqueInit())
        }
        result.append(row)
    }
    return result
}  

This function does not compile. The statement

row.append(T.uniqueInit())  

gives the errors:

Argument type 'T.T' does not conform to expected type 'UniqueInit'  
Cannot convert value of type 'T.T' (associated type of protocol 'UniqueInit') to expected argument type 'T' (generic parameter of static method 'dataSource(nrRowsInSection:)')  

Obviously, static func uniqueInit() is considered as wrong, but why?
And what would be the correct implementation?

Upvotes: 3

Views: 1800

Answers (2)

user4527951
user4527951

Reputation:

See if the below implementation works for you.

protocol UniqueInit {
    static func uniqueInit() -> Self
}

func dataSource<T: UniqueInit>(nrRowsInSection: Int...) -> [[T]] {
    var result: [[T]] = []
    for nrRows in nrRowsInSection {
        var row: [T] = []
        for _ in 0 ..< nrRows {
            row.append(T.uniqueInit())
        }
        result.append(row)
    }
    return result
}

I think the T in the above dataSource implementation should be replaced with UniqueInit.

Upvotes: 1

dan
dan

Reputation: 9825

The generic T in your function and the associated type T in your protocol aren't the same T. Inside the function, T is referring to the type that is implementing the protocol so the associatedtype is T.T inside of the function. Your arrays and the return value would have to be using T.T.

This also means that you will need an additional parameter to your function because the [[T.T]] return value isn't enough for the compiler to infer what type T is.

This should work (I changed the generic parameter to U because all the Ts are confusing):

func dataSource<U: UniqueInit>(initializer: U.Type, nrRowsInSection: Int...) -> [[U.T]] {
    var result: [[U.T]] = []
    for nrRows in nrRowsInSection {
        var row: [U.T] = []
        for _ in 0 ..< nrRows {
            row.append(U.uniqueInit())
        }
        result.append(row)
    }
    return result
}

Alternatively, you could define your function as an extension on UniqueInit which would eliminate the need for the generics:

extension UniqueInit {
    func dataSource(nrRowsInSection: Int...) -> [[T]] {
        var result: [[T]] = []
        for nrRows in nrRowsInSection {
            var row: [T] = []
            for _ in 0 ..< nrRows {
                row.append(Self.uniqueInit())
            }
            result.append(row)
        }
        return result
    }
}

Upvotes: 2

Related Questions