Swift 5: почему диапазон.Bound.utf16Offset вычисляет смещения, даже если в качестве аргумента указана пустая строка

#swift

#swift

Вопрос:

В Swift 4 у меня была функция для сброса диапазона в выходные данные отладчика, определенные следующим образом:

 extension Range where Bound == String.Index {

   public func dump() -> String {
      return "[(lowerBound.encodedOffset)..<(upperBound.encodedOffset)] ((length))"
   }

   public var length: Int {
      return upperBound.encodedOffset - lowerBound.encodedOffset
   }
}
  

Поскольку в Swift 5 encodedOffset не рекомендуется, я изменил реализацию, как показано ниже:

 extension Range where Bound == String.Index {

   public func dump(string: String) -> String {
      let lower = lowerBound.utf16Offset(in: string)
      let upper = upperBound.utf16Offset(in: string)
      let result = "[(lower)..<(upper)] ((upper - lower))"
      return result
   }
}
  

Тест работает должным образом после обновления реализации:

 class RangeTests: LogicTestCase {

   func test_dump() {
      let string = "Hello!"
      let range = string.range(of: "ello")
      Assert.equals(range?.dump(string: string), "[1..<5] (4)")
   }
}
  

Но функция dump работает корректно, даже если передается пустая строка:

 class RangeTests: LogicTestCase {

   func test_dump() {
      let string = "Hello!"
      let range = string.range(of: "ello")
      Assert.equals(range?.dump(string: ""), "[1..<5] (4)")
   }
}
  

Не следует, например, вызывать lowerBound.utf16Offset(in: "") исключение throw, потому что мы передаем пустую строку, в то время как сам диапазон не пуст?


ОБНОВЛЕНИЕ 1:

С помощью трюка, lowerBound.utf16Offset(in: "") упомянутого выше, следующие две версии функции сдвига индекса образца работают идентично:

 extension String.Index {

   // Deprecated due `encodedOffset`
   public func shifting(by offset: Int) -> String.Index {
      return String.Index(encodedOffset: encodedOffset   offset)
   }

   // Possible Swift 5 replacement to achieve index manipulation
   // without reference to string. 
   public func shifting(by offset: Int) -> String.Index {
      let newOffset = utf16Offset(in: "")   offset
      let referenceString = String(repeating: " ", count: newOffset)
      let result = String.Index(utf16Offset: newOffset, in: referenceString)
      return result
   }
}
  

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

1. Не используйте кодированное смещение, используйте метод string distance. Попробуйте "😀😀😀".lastIndex(of: "😀")?.encodedOffset , чтобы вернуть 4

2. почему вы не используете свойство range count ?

3. range.count причина ошибки Type 'String.Index' does not conform to protocol 'Strideable' : 0

4. моя ошибка. Для вычисления расстояния вам нужен экземпляр строки

5. let string = "😀😀😀" let range = string.startIndex..<string.endIndex let distance = string.distance(from: range.lowerBound, to: range.upperBound) print(distance) // 3