протокол с таким же именем связанного типа

Если у меня есть два протокола, связанный тип которых совпадает, например

protocol Read {
    associatedtype Element
    func read() -> Element
}
protocol Write {
    associatedtype Element
    func write(a: Element)
}

Тогда я хотел бы иметь класс для чтения целого числа и записи строки в:

class ReadWrite: Read, Write {
    func read() -> Int {
        return 5
    }
    func write(a: String) {
        print("writing \(a)")
    }
}

но компилятор жалуется и предлагает заменить String на Int. В идеале тип должен быть выведен или, по крайней мере, компилирован, если я явно объявляю

associatedtype Read.Element = Int
associatedtype Write.Element = String

в пределах ReadWrite. Любая работа?

Обновить

Обходной путь, вдохновленный этим вопросом, заключается в создании двух вспомогательных протоколов.

protocol ReadInt: Read {
    associatedtype Element = Int
}
protocol WriteString: Write {
    associatedtype Element = String
}

и вместо этого унаследовать класс от этих двух:

class ReadWrite: ReadInt, WriteString {
    func read() -> Int {
        return 5
    }
    func write(a: String) {
        print("writing \(a)")
    }
}

Кажется, это компилируется, но я боюсь, что в этом случае возникнут какие-либо ошибки.

обновить снова

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


person Franklin Yu    schedule 09.06.2016    source источник
comment
У вас есть протоколы чтения и записи? Если это так, вы можете просто изменить имя связанных типов на что-то вроде ReadElement и WriteElement.   -  person Mike Taverne    schedule 10.06.2016
comment
@MikeTaverne правда, но я чувствую стресс, когда пишу библиотеки, где мне нужно вручную префикс имени моего связанного типа (например, FYMyLibElement. Эта проблема не такая уж редкость, например, в пакете Foundation есть много протоколов, использующих одни и те же связанные имя типа Элемент.   -  person Franklin Yu    schedule 10.06.2016
comment
Возможно ли реализовать два таких протокола Foundation в одном классе, где в реализации каждого протокола используется другой тип?   -  person Mike Taverne    schedule 10.06.2016
comment
По-прежнему проблема в Swift 3 / Xcode 8. Думали ли вы о том, чтобы отправить отчет об ошибке?   -  person Mike Taverne    schedule 16.06.2016
comment
Хм ... Еще нет. Следует ли мне отправить его сюда? О, уже существует проблема.   -  person Franklin Yu    schedule 16.06.2016
comment
@MikeTaverne Я голосовал за вопрос; Думаю, это все, что я могу сделать на данный момент.   -  person Franklin Yu    schedule 16.06.2016
comment
Я думаю, что в языке есть общая проблема: в нем отсутствуют средства для использования полных имен. Описанная проблема - лишь одна из многих проблем, возникающих из-за конфликтов имен, которые можно было бы легко решить, если бы была возможность использовать квалифицированные имена. Между прочим, я автор проблемы в системе отслеживания проблем Swift. Пожалуйста, проголосуйте, если хотите улучшить язык;)   -  person CouchDeveloper    schedule 16.06.2016
comment
@CouchDeveloper Я не уверен, {компилятор действительно использует полные имена под капотом, но не предоставляет прямой доступ к нему из кода Swift} или {компилятор вообще не использует полные имена}. Если это последнее, я бы сказал, что эта функция (связанный тип) тщательно продумана. Эта проблема никогда не возникала в твердых языках, таких как Java или C ++. Однако я помню, что где-то видел что-то вроде Self.Generator.Element, поэтому я считаю, что нам просто нужно запросить прямой доступ к квалифицированному имени.   -  person Franklin Yu    schedule 16.06.2016
comment
@FranklinYu Полностью согласен!   -  person CouchDeveloper    schedule 17.06.2016
comment
Наличие большого количества протоколов в проекте кажется опасным ограничением. Я добавил свой голос к проблеме - спасибо, что предоставили ссылку.   -  person Steffen D. Sommer    schedule 10.08.2016


Ответы (1)


Другой обходной путь - создать третий комбинированный протокол:

protocol ReadWrite {
    associatedtype R
    associatedtype W
    func read() -> R
    func write(a: W)
}

Это некрасиво, поскольку вынуждает повторно объявлять членов протокола, но сохраняет общий характер (вы не ограничены String и Int).

person Mike Taverne    schedule 10.06.2016
comment
Это решение напоминает мне этот вопрос; Я склонен рассматривать это как ошибку компилятора. - person Franklin Yu; 10.06.2016
comment
У этого решения есть один недостаток: если более двух протоколов используют одно и то же имя связанного типа, мне нужно иметь комбинированный протокол для каждой возможной комбинации между ними, поэтому я оставлю выбор пользователю. - person Franklin Yu; 11.06.2016
comment
Кажется, что компилятор не может различить связанные типы, определенные в разных протоколах с одним и тем же именем. Область действия имени связанного типа расширяется за пределы протокола, в котором оно было определено. - person Mike Taverne; 11.06.2016
comment
Думал, что под капотом Element переименовывается в Read.Element или Write.Element. Кажется, что в настоящее время компилятор загрязняет текущее пространство имен всеми связанными типами из принятых протоколов (и их родительских протоколов), не так ли? Или это просто так язык указан? - person Franklin Yu; 11.06.2016
comment
На самом деле это не обходной путь: предположим, есть два API, один из которых требует параметра, соответствующего Read, другой - с параметром, соответствующего Write. Невозможно иметь одну конкретную структуру или класс, который бы соответствовал обоим. Ни в коем случае не означает, что это невозможно выразить на Swift. Облом. - person CouchDeveloper; 16.06.2016
comment
@CouchDeveloper. Вы правы, это не обходной путь для потребителя. Я предлагал обходной путь, если вы являетесь автором протоколов. Хотя в этом случае более простым решением, о котором я также упоминал, является уникальное наименование связанных типов. - person Mike Taverne; 16.06.2016