Swift добавляет расширение ограничения к протоколу, имеющему связанный тип

#swift #protocols #associated-types

Вопрос:

Когда я добавляю расширение ограничения к протоколу, имеющему связанный тип, компилятор swift игнорирует мое ограничение.

Когда я пишу:

 protocol Arr {  associatedtype Element   func node(_ at: Int) -gt; Element? }  extension Arr where Element == String {  func node(_ at: Int) -gt; String? {  nil  } }  struct Doo: Arr { }  

Xcode успешно строится, и он думает, что у меня Doo Element String все в порядке . Он игнорирует where Element == String ограничение.

Когда я пишу:

 protocol Arr {  associatedtype Element   func node(_ at: Int) -gt; Element? }  extension Arr where Element == String {  func node(_ at: Int) -gt; Element? { // use Element  nil  } }  struct Doo: Arr { }  

Xcode показывает ошибку, как и ожидалось.

Это ошибка Xcode или функция Swift?

Версия Xcode: Version 13.1 (13A1030d)

Быстрая версия:

 swift-driver version: 1.26.9 Apple Swift version 5.5.1 (swiftlang-1300.0.31.4 clang-1300.0.29.6) Target: arm64-apple-macosx12.0  

Комментарии:

1. Я думаю, это связано с тем, что ограничения применяются гораздо позже (хотя у меня нет для этого никаких источников). Doo получил node метод «как только», как вы говорите : Arr . А потом он видит , что node возвращается String? , поэтому делает вывод, что Element так и должно быть String . Если node бы это было возвращено Element? , вывод был бы невозможен. Знаете ли вы, что Swift выводит связанные типы из сигнатур методов?

2. Ограничения проверяются только при попытке вызова node . Только тогда компилятор скажет: » node доступно только тогда, когда …»

3. The Xcode show an error for me expected — какой ошибки вы ожидаете?

4. @Cristik Xcode показать: Type 'Doo' does not conform to protocol 'Arr'

Ответ №1:

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

Например, предположим, что расширение не существовало бы, и Doo определение было бы таким:

 struct Doo: Arr {  func node(_ at: Int) -gt; String? {  nil  } }  

, компилятор Swift с радостью заполнит Element соответствующий тип.

Аналогичная вещь происходит с вашим первым фрагментом кода, Doo соответствует Arr , и компилятор находит определение, которое удовлетворяет всем требованиям протокола. Swift не игнорирует Element == String ограничение, потому что связывает его со Doo структурой.

Если вы добавите второе расширение аналогичным образом, но для другого типа ( Int например,), вы увидите, что получите ожидаемую ошибку. Это происходит потому, что компилятор больше не может определить требования к протоколу.

Компилятор Swift охотно выводит как можно больше из контекста, к которому он может обратиться, в большинстве случаев дает отличные результаты, иногда не очень хорошие (особенно при работе с закрытиями), а иногда дает неожиданные результаты (например, этот).

Если вы хотите быть уверены, что компилятор выводит нужные вам типы, решением будет явное объявление всех задействованных типов.

Комментарии:

1. Спасибо вам за ваш ответ. Я тебя понимаю. Компилятор применяет функцию, которая в расширении, а затем проверяет ограничение, и все выполнено. В этом есть смысл. Но такое поведение очень странно, я постараюсь сообщить об ошибке в проект swift. Я думаю, что компилятор может сначала проверить ограничение, которое следует за расширением, и, если оно прошло, затем добавить функции или переменную в Doo структуру. Возможно, это может уменьшить количество ошибок, которые были добавлены неожиданно. Потому что в моем первом фрагменте кода Arr значение протокола Element по умолчанию равно String . Это неожиданно. И трудно найти эту причину.

2. Конечно, вы можете попробовать отправить сообщение об ошибке, если вы считаете, что это неправильное поведение, @sunny. В основном это стремление компилятора, которое вызывает это, быть чрезмерно полезным иногда кажется не лучшим 🙂

Ответ №2:

Ошибки во втором случае возникают из-за того, что Xcode не может определить тип элемента. Если вы укажете это, все компилируется.

 struct Doo: Arr {  typealias Element = String   }  

Комментарии:

1. Вопрос не в том, почему возникает ошибка или как ее исправить. Все наоборот, о том, почему в первом фрагменте кода нет ошибки.

2. В первом случае компилятор может определить тип Element , потому что функция node , как говорят, возвращает String .