Почему мое расширение протокола Swift не может обернуть существующую функцию того же типа?

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

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

Это казалось простым расширением техники, представленной в этом ответе. документация оператора индекса коллекции очень проста:

subscript(bounds: Range<Self.Index>) -> Slice<Self> { get }

Но когда я использую те же типы в своей функции-оболочке, я получаю следующее:  snippet

Копировать / вставить версию:

extension Collection where Indices.Iterator.Element == Index {
    subscript(safe bounds: Range<Self.Index>) -> Slice<Self> {
        let empty = Slice(base: self, bounds: (startIndex..<startIndex))
        guard bounds.lowerBound < endIndex else { return empty }
        guard bounds.upperBound >= startIndex else { return empty }

        let lo = Swift.max(startIndex, bounds.lowerBound)
        let hi = Swift.min(endIndex, bounds.upperBound)
        return self[lo..<hi]
    }
}

Почему я не могу подписать коллекцию таким образом? Почему компилятор подтверждает, что я использую правильный тип Range<Self.Index> (указанный в документации), но все же считаю это ошибкой?


person Ian    schedule 08.12.2016    source источник
comment
Обратите внимание, что ваше ограничение where Indices.Iterator.Element == Index не используется в вашем настраиваемом подстрочном индексе. Вы можете рассмотреть возможность переноса его в неограниченное расширение Collection (при условии, что у вас есть другие методы, которые используют ограничение в вашем текущем).   -  person Hamish    schedule 08.12.2016
comment
Проще говоря, ответ Хэмиша: self[lo..<hi] - это Self.SubSequence, поэтому вы не можете вернуть его там, где ожидается Slice<Self>. Конечно, сообщение об ошибке (как это часто бывает) вводит в заблуждение.   -  person matt    schedule 08.12.2016
comment
@Hamish Я думаю, мне нужно это ограничение для другой функции в моем расширении (в этом ответе), но даже тогда - в соответствии с моим комментарием - я не понимаю, как это устроено   -  person Ian    schedule 09.12.2016
comment
@Ian Как ни странно, это ограничение было предложено мной в этом ответе, который упоминается в ответе, на который вы ссылаетесь. Вы можете увидеть определение Indices в этом ответе, и вы можете видеть, что он не дает никаких обещаний относительно типа своих элементов (поэтому компилятор не может узнать, являются ли они Equatable, и, следовательно, можете ли вы использовать contains(_:) метод на .indices).   -  person Hamish    schedule 09.12.2016
comment
Однако, как только для связанных типов будут разрешены предложения where, я надеюсь, что команда stdlib добавит один в Indices, который ограничивает его Iterator.Element типом Index (потому что это то, что это, набор индексов), что позволит вам избавиться от с ограничением расширения. До тех пор в моем первом комментарии я предлагал иметь одно неограниченное расширение Collection для ранжированного нижнего индекса, а затем иметь другое расширение Collection, на этот раз с where Indices.Iterator.Element == Index для единственного нижнего индекса.   -  person Hamish    schedule 09.12.2016


Ответы (1)


Требование ранжированного нижнего индекса Collection возвращает _ 2_ (связанный тип):

subscript(bounds: Range<Self.Index>) -> Self.SubSequence { get }

Подстрочный индекс , на который вы ссылаетесь:

subscript(bounds: Range<Self.Index>) -> Slice<Self> { get }

это просто реализация по умолчанию этого нижнего индекса, где SubSequence - это Slice<Self>. Любой тип, соответствующий Collection, может реализовать это требование по-другому - им не обязательно определять свой SubSequence как Slice<Self>.

Поэтому вам просто нужно изменить свое расширение, чтобы отразить, что SubSequence возвращается из используемого нижнего индекса:

extension Collection {
    subscript(safe bounds: Range<Index>) -> SubSequence {

        // as the method returns a SubSequence,
        // use the regular subscript to get an empty SubSequence
        let empty = self[startIndex..<startIndex]

        guard bounds.lowerBound < endIndex else { return empty }
        guard bounds.upperBound >= startIndex else { return empty }

        let lo = Swift.max(startIndex, bounds.lowerBound)
        let hi = Swift.min(endIndex, bounds.upperBound)

        return self[lo..<hi]
    }
}
person Hamish    schedule 08.12.2016
comment
Итак, если я правильно это понимаю, ошибка компилятора отражает идею о том, что возвращение Slice<Self> не произойдет с операцией subscript, которую я пытаюсь выполнить? - person Ian; 08.12.2016
comment
@Ian Да, ошибка компилятора сообщает вам (довольно неудачно), что он не может найти требование subscript в протоколе Collection, которое возвращает Slice<Self>, поскольку требование в протоколе возвращает SubSequence, а не Slice<Self>. - person Hamish; 09.12.2016
comment
Хотя существует реализация по умолчанию для требования индекса, которое возвращает Slice<Self>, компилятор не может гарантировать, что он существует для произвольной коллекции (вы, конечно, можете ограничить расширение так, чтобы SubSequence был Slice<Self> , что позволило бы вашему текущему коду работать, но я не вижу практических причин для введения этого ограничения). - person Hamish; 09.12.2016
comment
Похоже, настоящая проблема здесь в том, что я пролистал справочную страницу коллекции и не понимал, что смотрю на определение реализации по умолчанию, а не на определение протокола. - person Ian; 09.12.2016