Не удается преобразовать значение универсального ассоциированного типа протокола в ожидаемый тип аргумента.

Чтобы изучить дженерики 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() считается неправильным, но почему?
И какой будет правильная реализация?


person Reinhard Männer    schedule 02.07.2018    source источник


Ответы (2)


Общий 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
    }
}
person dan    schedule 02.07.2018
comment
Спасибо за Ваш ответ. Я кратко проверю ваше предложение. - person Reinhard Männer; 02.07.2018
comment
Я проверил ваше 1-е решение, оно работает, спасибо! Теперь я понимаю, что 2 T в моей реализации разные. - person Reinhard Männer; 02.07.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 в приведенной выше реализации dataSource следует заменить на UniqueInit.

person Community    schedule 02.07.2018