I have two ways of writing the same code, one of which seems to be disliked by the Swift compiler. Can you please explain why?
Context:
let guaranteedValue: String
let cursorPositionFromEnd: Int
Working code:
let stringFromEndUntilCursorPosition = String(guaranteedValue.reversed()[0..<cursorPositionFromEnd])
Non-working code:
let reversedOriginalString = guaranteedValue.reversed()
let stringFromEndUntilCursorPosition = String(reversedOriginalString[0..<cursorPositionFromEnd])
Compiler error message: "Cannot subscript a value of type ReversedCollection<String>
with an index of type Range<Int>
"
Other working attempt:
let reversedOriginalString = guaranteedValue.reversed()[0..< cursorPositionFromEnd]
let stringFromEndUntilCursorPosition = String(reversedOriginalString)
Basically the idea is you can only subscript a reversed range {but probably not only} if you add the index at a function return, but that does not work if you first reference the variable with a let or var and then try subscripting it.
I also understand that it would probably work in the "non-working code" if the Range would be String.Index type or whatever is the new fashion of doing it.
Can anyone explain why? Is this a bug in Swift's compiler which already has enough "twisted" string logic?
There are several reversed()
methods, as explained in What trouble could bring assining reversed() to a swift array?.
In your first code example,
let stringFromEndUntilCursorPosition = String(guaranteedValue.reversed()[0..<cursorPositionFromEnd])
the compiler infers from the context (i.e. the subscript) that the
Sequence.reversed()
method is needed, which returns an array. Arrays are indexed by integers,
therefore the code compiles. This method has O(n)
complexity because a
new array with all elements is created.
In
let reversedOriginalString = guaranteedValue.reversed()
there is no such context, and the compiler chooses the
Bidirectional.reversed()
method. Compared to the above mentioned method, this one has
O(1)
complexity. It returns a ReversedCollection
which
has its own index type, therefore
let stringFromEndUntilCursorPosition = String(reversedOriginalString[0..<cursorPositionFromEnd])
produces the observed error message.
Possible solutions:
Provide context to enforce the array creation:
let reversedOriginalString: [Character] = guaranteedValue.reversed()
// Or: let reversedOriginalString: Array = guaranteedValue.reversed()
// Or: let reversedOriginalString = guaranteedValue.reversed() as Array
let stringFromEndUntilCursorPosition = String(reversedOriginalString[0..<cursorPositionFromEnd])
This works, but has the disadvantage of creating a temporary array.
Do the proper index calculations on the reversed collection:
let reversedOriginalString = guaranteedValue.reversed()
let pos = reversedOriginalString.index(reversedOriginalString.startIndex, offsetBy: cursorPositionFromEnd)
let stringFromEndUntilCursorPosition = String(reversedOriginalString[..<pos])
This avoids the intermediate array, but is tedious to write.
Use the prefix(maxLength:)
method of sequences:
let reversedOriginalString = guaranteedValue.reversed()
let stringFromEndUntilCursorPosition = String(reversedOriginalString.prefix(cursorPositionFromEnd))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With