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.
source to share
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.
source to share