Как использовать ассоциированный тип из универсального в качестве типа?

У меня есть такой код:

protocol CoreTypes {
    associatedtype TypeA
    // ...more types and various operations between them
}

protocol Context<Types> {
    associatedtype Types: CoreTypes

    var valueA: Types.TypeA { get }
    func setValueA(_ b: Types.TypeA)
}

class Operation<Types: CoreTypes> {
    var context: any Context<Types>

    init(context: some Context<Types>) {
        self.context = context
    }

    func perform() {
        context.setValueA(context.valueA)
    }
}

Однако context.setValueA(context.valueA) не компилируется. Ошибка:

Невозможно преобразовать значение типа «Любой» в ожидаемый тип аргумента «Types.TypeA».

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

var valueA: Types.TypeA { get }
func setValueA(_ b: Types.TypeA)

Я знаю, что могу решить эту конкретную проблему, определив Context<TypeA>, но мне нужно использовать его косвенно, т. е. из Types.TypeA. Есть ли способ заставить это работать?


Это также отлично работает, если я конвертирую Context в класс, т.е.:

class Context<Types: CoreTypes> {
    var valueA: Types.TypeA { preconditionFailure() }
    func setValueA(_ b: Types.TypeA) { preconditionFailure() }
} 

Таким образом, я могу удалить any из var context: Context<Types>, но использование этих «фальшивых абстрактных классов» делает вещи менее безопасными во время компиляции...

Итак, есть ли лучший способ использовать AssociateType из другого типа?

Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
57
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Вы можете написать общую вспомогательную функцию, чтобы открыть экзистенциальный тип (SE-0352):

func perform() {
    func helper<T: Context<Types>>(_ x: T) {
        x.setValueA(x.valueA)
    }
    helper(context)
}

Тем не менее, насколько я понимаю, SE-0353, context.setValueA(context.valueA) также должно быть возможным, поскольку связанный тип Context ограничен конкретным типом.

context.setValueA(context.valueA) компилируется, если бы это было:

var valueA: Types { get }
func setValueA(_ b: Types)

Так что вполне возможно, что команда Swift не учла этот случай при разработке/реализации SE-0353.

Спасибо! Удивительно, но это helper действительно работает! Это очень изящный трюк для преобразования any в some.

Paulius Liekis 13.04.2024 08:41

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

protocol Context<Types, TypeA> where TypeA == Types.TypeA {
    associatedtype Types: CoreTypes

    var valueA: Types.TypeA { get }
    func setValueA(_ b: Types.TypeA)
}

class Operation<Types: CoreTypes> {
    var context: any Context<Types, Types.TypeA>

    init(context: some Context<Types, Types.TypeA>) {
        self.context = context
    }

    func perform() {
        context.setValueA(context.valueA)
    }
}

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

Похожие вопросы

Почему список быстрой навигации с кнопкой открывает лист несколько раз, а не только один раз?
В быстром расширении статического var, как вы можете получить доступ к конкретному классу и вернуть его?
Как отобразить несколько данных за один месяц с помощью библиотеки диаграмм SwiftUI на гистограмме?
Развертывание JSON вручную без кодирования – Swift
Глубокая ссылка не обеспечивает переход к ожидаемому местоположению, если приложение удалено из памяти в быстрой iOS
Продукт пакета Swift не найден при добавлении через зависимость Package.swift
Состояние SwiftUI Bool не обновлено или неверно
Неправильное положение кривой нижнего правого угла (быстрая)
Выравнивание пользовательского интерфейса выглядит не очень хорошо, отображаются точки, как это исправить?
Класс SwiftData @Model, порядок переключения операторов в блоке инициализации неожиданно нарушит представление