Search code examples
swiftstringrange

Create a SUBSCRIPT EXTENSION for String with integer Range


This solution with two integers works, but I'd like to use a range instead:

extension String {
   subscript(start: Int, end: Int) -> String? {
      return String(self[index(startIndex, offsetBy: start#)...index(startIndex, offsetBy: end)])
   }
}

The two following solutions use ranges, but both produce the same error, not in the extension code, but when attempting to use it:

'subscript' is unavailable: cannot subscript String with an integer range.

This is an absurd error that's basically telling me there is no range subscript for String, when in fact I just created it, so it DOES exist.

extension String {
   subscript(range: Range<String.IndexDistance>) -> String? {
      return String(self[index(startIndex, offsetBy: range𝜮.startIndex)...index(startIndex, offsetBy: range𝜮.endIndex)])
   }

   subscript(range: Range<Int>) -> String? {
      return String(self[index(startIndex, offsetBy: range.startIndex)...index(startIndex, offsetBy: range.endIndex)])
   }
}

let greeting = "Hello, World"
print(greeting[0...4]) // should print "Hello"

Solution

  • The compiler error is not unexpected. The type of the range 0...4 is not Range<Int>, but ClosedRange<Int>, which are unrelated types. If you modify your subscript implementation to take a ClosedRange<Int> instead of a Range<Int>, your code compiles and works just fine.

    extension String {
        subscript(range: ClosedRange<Int>) -> String? {
            return String(self[index(startIndex, offsetBy: range.lowerBound)...index(startIndex, offsetBy: range.upperBound)])
        }
    }
    

    If you want to return nil in case any of the indices would produce a range out of bounds exception, you can use index(_:,offsetBy:,limitedBy:).

    extension String {
        subscript(range: ClosedRange<Int>) -> String? {
            guard let startIndex = index(startIndex, offsetBy: range.lowerBound,limitedBy: endIndex), let endIndex = index(startIndex, offsetBy: range.upperBound,limitedBy: endIndex) else { return nil }
            return String(self[startIndex...endIndex])
        }
    }