Why does swift give an error when I set offsetBy to endIndex in the index (_, offsetBy, limitedBy) function?

Here is the code:

1- It's good when offsetBy is 4 or less

    let someString = "hello"

    if let someIndex = someString.index(someString.startIndex, 
                        offsetBy: 4, limitedBy: someString.endIndex){
       someString[someIndex]
    }
    // Prints "o"

      

2- It's good when offsetBy is 6 or more

    if let someIndex = someString.index(someString.startIndex, 
                        offsetBy: 6, limitedBy: someString.endIndex){
       someString[someIndex]
    }
    // Prints "nil"

      

3- But it gives error when offsetBy is 5

    if let someIndex = someString.index(someString.startIndex, 
                        offsetBy: 5, limitedBy: someString.endIndex){
       someString[someIndex]
    }
    // error

      

and the error:

error: Execution of the playground interrupted: error: execution was interrupted, Reason: EXC_BAD_INSTRUCTION (code = EXC_I386_INVOP, subcode = 0x0). the process was left at the point where it was interrupted, use "thread return -x" to return to the state before evaluating.

+3


source to share


1 answer


The problem is that the limit given index(_:offsetBy:limitedBy:)

is an inclusive estimate for the outcome. Therefore at

let someString = "hello"

      

the index returned

someString.index(someString.startIndex, offsetBy: 5, limitedBy: someString.endIndex)

      

will someString.endIndex

, which is the past end index and therefore is not a valid index for indexing the row with.



So a simple solution would be to just add a condition to your statement if

to check that the index you are returning is not endIndex

:

let someString = "hello"
let offset = 5

if let someIndex = someString.index(someString.startIndex,
                                    offsetBy: offset,
                                    limitedBy: someString.endIndex
                                    ), someIndex != someString.endIndex {
    print(someString[someIndex])
}

      

Or, a nicer option would be to use a property CharacterView

indices

to get the set of valid indices for the index with (excluding endIndex

), and use dropFirst(_:)

and first

to get the index at a given offset:

if let index = someString.characters.indices.dropFirst(offset).first {
    print(someString[index])
}

      

This takes advantage of the fact that it dropFirst(_:)

takes the upper bound of the items to be discarded, returning an empty subsequence if it is greater than the collection count, and the fact that it first

returns nil

for an empty collection.

+3


source







All Articles