0

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"
Hikarus
  • 325
  • 4
  • 17
  • Did you have a look at the various answers in https://stackoverflow.com/q/24092884/1187415 (such as https://stackoverflow.com/a/38215613/1187415) ? – Martin R Feb 11 '19 at 14:07

2 Answers2

1

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])
    }
}
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • Nice. Thank you, David. How would you add a guard statement that compares range.endIndex to the count of the String to prevent an index-out-of-range error? The types seem to be incompatible. – Hikarus Feb 11 '19 at 15:16
  • oh... It doesn't quite work. I'm trying with the following code: let numbers = "0123456789" numbers[0...4] // correct: "01234" numbers[2...2] // incorrect: "234" – Hikarus Feb 11 '19 at 19:39
  • @Hikarus please specify "doesn't quite work". With my test inputs, it works just fine – Dávid Pásztor Feb 11 '19 at 19:40
  • sorry, I accidentally pressed enter before the comment was finished. I'm trying with [2...3] or [2], and it doesn't produce a correct result... – Hikarus Feb 11 '19 at 19:41
  • it's ok. I got it in the solution link :) – Hikarus Feb 11 '19 at 20:40
0

Correct answer base on @Dávid Pásztor's code:

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

Here is the where the mistake occurs

let startIndex = index(startIndex, offsetBy: range.lowerBound,limitedBy: endIndex)

// startIndex shall be self. startIndex, name does matter
let endIndex = index(startIndex, offsetBy: range.upperBound,limitedBy: endIndex) 
black_pearl
  • 2,549
  • 1
  • 23
  • 36