Быстрое соответствие нескольким протоколам, ошибка компиляции

У меня есть следующие протоколы:

protocol ProtoAInput {
    func funcA()
}

protocol ProtoA {
    var input: ProtoAInput { get }
}

protocol ProtoBInput {
    func funcB()
}

protocol ProtoB {
    var input: ProtoBInput { get }
}

Я хочу, чтобы мой StructC соответствовал обоим протоколам ProtoA и ProtoB только с одним свойством input. Что само по себе является просто ссылкой на self, так как StructC также реализует ProtoAInput и ProtoBInput в отдельном расширении.

struct StructC: ProtoA, ProtoB {
    var input: ProtoAInput & ProtoBInput { return self }
}

extension StructC: ProtoAInput {
    func funcA() { print("funcA") }
}

extension StructC: ProtoBInput {
    func funcB() { print("funcB") }
}

let s = StructC()
s.funcA()
s.funcB()

Компилятор Swift 5.3 не может собрать этот код со следующими ошибками:

Type 'StructC' does not conform to protocol 'ProtoA'

Type 'StructC' does not conform to protocol 'ProtoB'

Существуют ли какие-либо правила компиляции, которые этот код нарушает? Я не понимаю, почему я не могу иметь здесь переменную input, которая одновременно соответствует обоим протоколам.

Протоколы так не работают, читайте docs.swift.org/swift-book/LanguageGuide/Protocols.html.

Asperi 24.12.2020 17:00

Да, я читал про композицию протоколов, но все же не могу понять, почему такой код не поддерживается, есть ли в нем какая-то неопределенность, с которой компилятор не справляется?

danylokos 24.12.2020 17:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
129
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

struct StructC: ProtoA, ProtoB {
    var input: ProtoAInput & ProtoBInput { return self }
}

Вместо вышеуказанного вам нужно что-то вроде следующего

extension ProtoA where Self == StructC {
    var input: ProtoAInput { self }
}

extension ProtoB where Self == StructC {
    var input: ProtoBInput { self }
}

struct StructC: ProtoA, ProtoB {
}

Протестировано и работает с Xcode 12.1 / iOS 14.1

Ответ принят как подходящий

Это SR-522 (протокольные функции не могут иметь ковариантный возврат). Swift просто не поддерживает это. Если для протокола требуется тип, то это именно тот тип, который требуется. То же самое верно и для подклассов:

class ProtoAInput {}
class ProtoAInputSub: ProtoAInput {}

protocol ProtoA {
    var input: ProtoAInput { get }  // <=== requires superclass
}

struct StructA: ProtoA {
    var input: ProtoAInputSub // <=== So can't use a subclass here
}

Теоретически компилятор может поддерживать это, если требуемым свойством является только get. Если вы добавили set, это не сработает. Но это не работает сегодня ни для того, ни для другого.

Я подозреваю, что поддержка этого будет сложной из-за макетов экзистенциальных контейнеров. Вы не можете просто поставить P & Q в те же места, что и Q (с точки зрения реализации Swift, а не с точки зрения теории типов). Смещения в таблице-свидетеле будут неправильными. Кажется, что это можно реализовать, но я понимаю, почему это было бы сложно, не жертвуя производительностью за счет дополнительной косвенности.

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

Другие вопросы по теме