Соответствие протокола Swift при возврате универсального - PullRequest
1 голос
/ 04 августа 2020

Вот пример:

protocol Feed {
    func items<T>() -> [T]? where T: FeedItem
}

protocol FeedItem {}

class FeedModel: Feed, Decodable {
    func items<T>() -> [T]? where T : FeedItem {
        return [FeedItemModel]() // Error: Cannot convert return expression of type '[FeedItemModel]' to return type '[T]?'
    }
}

class FeedItemModel: FeedItem, Decodable {}

Почему:

A) пытается преобразовать в T, когда T является generi c, а не типом? Б) не распознает FeedItemModel как соответствующий FeedItem?

Ответы [ 3 ]

2 голосов
/ 04 августа 2020
func items<T>() -> [T]? where T : FeedItem

Это говорит о том, что вызывающий может определить T как угодно, пока T соответствует FeedItemModel, и эта функция вернет дополнительный массив из них.

FeedItemModel это что-то , которое соответствует FeedItem, но не обещано быть типом T, запрошенным вызывающим.

В качестве примера рассмотрим:

class OtherModel: FeedItem {}

Согласно вашей сигнатуре функции, я могу сделать это:

let ms: [OtherModel]? = FeedModel().items()

Но тогда ваша функция не вернет мне [OtherModel]?. Я подозреваю, что вы на самом деле не имеете в виду, что это generi c. Я ожидаю, что вы имеете в виду:

func items() -> [FeedItemModel]?

или, возможно,

func items() -> [FeedItem]?

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

1 голос
/ 04 августа 2020

Либо вы можете игнорировать дженерики, потому что они применимы только к этой одной функции и не нужны, поскольку прямое указание, что тип возвращаемого значения [FeedItem]? дает тот же результат

protocol Feed {
    func items() -> [FeedItem]?
}

class FeedModel: Feed, Decodable {
    func items() -> [FeedItem]?  {
        return [OtherModel]()
    }
}

Если вы используете с другой стороны, нужен общий протокол c, тогда вы должны использовать связанный тип

protocol Feed2 {
    associatedtype T: FeedItem

    func items() -> [T]?
}

class FeedModel2: Feed2, Decodable {
    typealias T = FeedItemModel
    func items() -> [T]?  {
        return [FeedItemModel]()
    }
}
1 голос
/ 04 августа 2020

A)

T - это тип, однородный бетонный тип, указанный во время выполнения. Изображение T равно class Foo : FeedItem очевидно, что FeedItemModel не может быть преобразовано в Foo

B)

FeedItemModel распознается как соответствующее FeedItem, но это не имеет значения .

Часто это смесь универсальных шаблонов и протоколов. Generi c типы не ковариантны. Если вам нужны ковариантные типы, используйте связанный тип.

...