Проблема со связанным типом, соответствующим универсальному классу в Swift - PullRequest
0 голосов
/ 10 апреля 2019

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

class C<T> {
    init(arg: T) {}
}

class SC<T>: C<T> {}

protocol P {
    associatedtype A: C<Self>
    func foo(_ x: A)
}

struct D: P {
    typealias A = C<D>
    func foo(_ x: A) {}
}

struct E: P {
    typealias A = SC<E>
    func foo(_ x: A) {}
}

Я ожидал бы, что этот код компилируется правильно.Вместо этого я получаю ошибки Type 'D' does not conform to protocol 'P' и Type 'E' does not conform to protocol 'P'.Xcode также предоставляет следующие сообщения: Possibly intended match 'D.A' (aka 'C<D>') does not inherit from 'C<Self>' и Possibly intended match 'E.A' (aka 'SC<E>') does not inherit from 'C<Self>'.

Эти ошибки не имеют смысла для меня.В первом случае C<D> должен быть того же типа, что и C<Self>, и поэтому C<D> должен соответствовать C<D> == C<Self>.Во втором случае C<Self> должно быть C<E>, поэтому SC<E> должно соответствовать C<E> == C<Self>.Это, очевидно, не относится к сообщениям Xcode.

Это ошибка компилятора / проблема с сообщениями об ошибках, или я неправильно понимаю, как в этом случае работают связанные типы?

1 Ответ

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

Я не думаю, что это проблема с вашим пониманием связанных типов, так же как и с дженериками.Обобщения не ковариантны;нет принципа замещения для параметризованных типов.Другими словами, то, что вы можете мысленно вставить D вместо Self, не означает, что вы можете заменить C<D> на C<Self>.Это разные типы (не связанные).

Вот простой контрпример, чтобы показать вам, что я имею в виду:

class C<T> {
    init(arg: T) {}
}
class Cat {}
class Kitten : Cat {}

Теперь давайте попробуем использовать C<Kitten>, где ожидается C<Cat>:

var c1 = C(arg:Cat())
var c2 = C(arg:Kitten())
c1 = c2 // error

Я подозреваю, что , что - это то, что, по вашему мнению, должно работать.Это не так.По той же причине ваша вера в то, что «C<D> должен быть того же типа, что и C<Self>», неверна.

Чтобы сделать это немного более релевантным вашему фактическому примеру, рассмотрим этот тривиальный случай.Это компилирует:

class C<T> {
    init(arg: T) {}
}
protocol P {
    associatedtype A: C<Any>
    func foo(_ x: A)
}
struct D: P {
    func foo(_ x: C<Any>) {}
}

Но это не компилируется:

class C<T> {
    init(arg: T) {}
}
protocol P {
    associatedtype A: C<Any>
    func foo(_ x: A)
}
struct D: P {
    func foo(_ x: C<String>) {}
}

Ну, вы можете сказать, что String - Any, так почему же этот компилятор не компилируется?Это потому, что этот факт не имеет значения;в параметризованном типе нет универсального принципа замещения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...