#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
.