Почему я должен отбросить соответствие протокола в классе Swift для расширения массива? - PullRequest
0 голосов
/ 08 апреля 2019

Я реализую модель дерева. Каждый тип узла может иметь широкий выбор возможностей. Я реализую это с помощью базы class Node {}, а затем многих протоколов с реализациями по умолчанию для описания возможностей. Затем у меня есть последние классы, производные от Node и соответствующие некоторому списку протоколов, для описания моей структуры.

Каждый протокол имеет связанный код в следующем стиле:

protocol Protocol {}

typealias ProtocolConformingNode = Node & Protocol

extension Node
{
    public var isProtocolConforming: Bool {
        return asProtocolConforming != nil
    }

    public var asProtocolConforming: ProtocolConformingNode? {
        return self as? ProtocolConformingNode
    }
}

Я использую Node & Protocol, чтобы я мог использовать другие связанные с протоколом запросы для возвращаемых элементов, например, asOtherProtocolConforming.

Это все работает нормально, пока я не буду иметь дело с массивами ProtocolConformingNode. Сначала я столкнулся с проблемами фильтрации массивов узлов + протокола, где я хотел сохранить соответствие после фильтрации:

extension Array where Element: Node
{
    var someFilter: [Element] {
        return self
    }
}

let array = [ProtocolConformingNode]()
let filteredArray = array.someFilter

Это дает следующую ошибку:

[ProtocolConformingNode]' (aka 'Array<Node & Protocol>') requires that 'ProtocolConformingNode' conform to 'AnyObject'

Это было немного странно, но я думаю, что подпадает под вопрос, который обсуждался ранее на SO: Протокол не соответствует самому себе?

Я решил, что стирание типов всех моих протоколов будет слишком большим для редких случаев, когда я использовал это, поэтому я был бы доволен приведением своих узлов после фильтрации. Обратите внимание, что я не использую Element в следующем коде, но явно использую Node и Element == Node:

extension Array where Element == Node
{
    var someFilter: [Node] {
        return self
    }
}

let array = [ProtocolConformingNode]()
let filteredArray = array.someFilter as! [ProtocolConformingNode]

Это теперь дает ошибку:

'[ProtocolConformingNode]' (aka 'Array<Node & Protocol>') is not convertible to 'Array<Node>'

Что странно для меня. Я могу заставить это работать так:

let filteredArray = (array as [Node]).someFilter as! [ProtocolConformingNode]

Но почему я должен читать здесь? Как это работает ниже?

var nodes = [Node]()
var protocolConformingNodes = [ProtocolConformingNode]()
nodes.append(contentsOf: protocolConformingNodes)

Когда:

mutating func append<S>(contentsOf newElements: S) where Element == S.Element, S : Sequence

1 Ответ

0 голосов
/ 10 апреля 2019

Это та же проблема, что и сообщение SO, на которое вы ссылались. В частности, вы должны прочитать ответ Хэмиша. ИМО, это недостаток языка, который должен быть проблемой только в том случае, если Protocol имеет статические требования или требования инициализатора, а в этом случае это не так.

Быстрое и грязное решение - использовать @objc. Полный код будет выглядеть как

class Node {}

@objc protocol Protocol {}

typealias ProtocolConformingNode = Node & Protocol

extension Array where Element: Node
{
    var someFilter: [Element] {
        return self
    }
}

let array = [ProtocolConformingNode]()
let filteredArray = array.someFilter // Yay! No error this time.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...