Невозможно преобразовать значение общего ассоциированного типа протокола в ожидаемый тип аргумента - PullRequest
0 голосов
/ 02 июля 2018

Для изучения обобщенных шаблонов Swift я написал функцию, которая создает источник данных tableView, то есть массив элементов размером 2 дим (разделы, строки). Тип элемента должен быть универсальным, а созданный источник данных должен быть инициализирован уникальными значениями элементов.

Я объявил протокол, принятый возможными типами элементов:

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

и функция dataSource.
Здесь nrRowsInSection - это переменный параметр: количество аргументов определяет количество секций, а значения аргументов определяют количество строк в соответствующем разделе.

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
}  

Эта функция не компилируется. Заявление

row.append(T.uniqueInit())  

выдает ошибки:

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:)')  

Очевидно, static func uniqueInit() считается неправильным, но почему?
И какая будет правильная реализация?

Ответы [ 2 ]

0 голосов
/ 02 июля 2018

Посмотрите, работает ли нижеприведенная реализация для вас.

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
}

Я думаю, что T в приведенной выше реализации источника данных должен быть заменен на UniqueInit.

0 голосов
/ 02 июля 2018

Общий T в вашей функции и связанный тип T в вашем протоколе не совпадают T. Внутри функции T относится к типу, реализующему протокол, поэтому связанный тип - T.T внутри функции. Ваши массивы и возвращаемое значение должны будут использовать T.T.

Это также означает, что вам понадобится дополнительный параметр для вашей функции, поскольку возвращаемое значение [[T.T]] недостаточно для того, чтобы компилятор мог определить, какой тип T.

Это должно работать (я изменил универсальный параметр на U, потому что все T сбивают с толку):

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
}

В качестве альтернативы вы можете определить свою функцию как расширение для UniqueInit, что устранит необходимость в дженериках:

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
    }
}
...