Why Does String.Subscript(_:) Require the Types 'String.Index' and 'Int' to Be Equal When There Is No 'Int' Involved

Why does String.subscript(_:) require the types `String.Index` and `Int` to be equal when there is no `Int` involved?

The current version of Swift works with the Substring struct which is a sliced String.

The error seems to be misleading and occurs if you are going to assign a (range-subscripted) Substring to a String variable.

To fix the error create a String from the Substring

iteration.template = String(template[iterationSubstring.endIndex...substring.startIndex])

Nevertheless you are strongly discouraged from creating ranges with indices from different strings (iterationSubstring and substring). Slice the main string, the indices are preserved.


The crash in the second (meanwhile deleted) example occurred because the last character of a string is at index before endIndex, it's

template[template.startIndex..<template.endIndex] 

or shorter

template[template.startIndex...]

Swift 5.1 Substring Issue

This is again one of those confusing error messages that doesn't tell you want you actually did wrong.

You should do this:

return String(s[start..<end])

This is because the subscript that takes a Range<String.Index> actually returns a Substring, but your method returns a String, so you have to convert it before returning.

Speculation on why the error message is outputted:

Seeing that the method returns a String, the Swift compiler tries to find a subscript which returns a String, and some how it found one (I couldn't), but that overload only works on types with the Index associated type being Int.

Why I can print a String, but throws error when I try to store it in Swift 5?

This is actually a bug, the error is misleading.

You have to create a String from the Substring, annotating the type is not sufficient.

if let beginning = test1, let end = test2 {
let why = String(websiteContent[beginning.lowerBound..<end.upperBound])
}

Side note:

You can omit , range: websiteContent.startIndex..<websiteContent.endIndex, the range of the whole string is the default.

What is a correct way to substring a string with NSRegularExpression results?

You should use subscript(_:)

return String(intext[mrange])

subscript' is unavailable: cannot subscript String with a CountableClosedRange Int , see the documentation comment for discussion

  1. If you want to use subscripts on Strings like "palindrome"[1..<3] and "palindrome"[1...3], use these extensions.

Swift 4

extension String {
subscript (bounds: CountableClosedRange<Int>) -> String {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return String(self[start...end])
}

subscript (bounds: CountableRange<Int>) -> String {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(startIndex, offsetBy: bounds.upperBound)
return String(self[start..<end])
}
}

Swift 3

For Swift 3 replace with return self[start...end] and return self[start..<end].


  1. Apple didn't build this into the Swift language because the definition of a 'character' depends on how the String is encoded. A character can be 8 to 64 bits, and the default is usually UTF-16. You can specify other String encodings in String.Index.

This is the documentation that Xcode error refers to.

More on String encodings like UTF-8 and UTF-16

Understanding slicing

The syntax is:

a[start:stop]  # items start through stop-1
a[start:] # items start through the rest of the array
a[:stop] # items from the beginning through stop-1
a[:] # a copy of the whole array

There is also the step value, which can be used with any of the above:

a[start:stop:step] # start through not past stop, by step

The key point to remember is that the :stop value represents the first value that is not in the selected slice. So, the difference between stop and start is the number of elements selected (if step is 1, the default).

The other feature is that start or stop may be a negative number, which means it counts from the end of the array instead of the beginning. So:

a[-1]    # last item in the array
a[-2:] # last two items in the array
a[:-2] # everything except the last two items

Similarly, step may be a negative number:

a[::-1]    # all items in the array, reversed
a[1::-1] # the first two items, reversed
a[:-3:-1] # the last two items, reversed
a[-3::-1] # everything except the last two items, reversed

Python is kind to the programmer if there are fewer items than you ask for. For example, if you ask for a[:-2] and a only contains one element, you get an empty list instead of an error. Sometimes you would prefer the error, so you have to be aware that this may happen.

Relationship with the slice object

A slice object can represent a slicing operation, i.e.:

a[start:stop:step]

is equivalent to:

a[slice(start, stop, step)]

Slice objects also behave slightly differently depending on the number of arguments, similarly to range(), i.e. both slice(stop) and slice(start, stop[, step]) are supported.
To skip specifying a given argument, one might use None, so that e.g. a[start:] is equivalent to a[slice(start, None)] or a[::-1] is equivalent to a[slice(None, None, -1)].

While the :-based notation is very helpful for simple slicing, the explicit use of slice() objects simplifies the programmatic generation of slicing.

same subscript code, when building it with two Separate lines it's working fine, when building it with one line of code, im getting an error , why?

Your example:

let str = createClass("someStr")["0"]{"One"}

is using trailing closure syntax.

Trailing closure syntax works by including the trailing closure as an additional parameter to a function call. Subscripting an array is really a function call under the hood (to a function called subscript), and Swift is trying to pass that closure as a second parameter to the subscripting call, which is what the error is explaining:

Cannot subscript a value of type 'SomeClass' with an argument of type '(String, () -> String)'.

In other words, you can't pass both "0" and the closure {"One"} to the subscripting function.


There are at least 3 ways to fix this and still put it on one line:

Option 1: Use an explicit call to pass the closure instead of using trailing closure syntax

Wrap the closure in () to make the call explicit:

let str1 = createClass("someStr")["0"]({"One"})
print(str1)

Option 2: Wrap the createClass("someStr")["0"] in parentheses

That lets Swift know the subscripting only gets "0" as a parameter and allows trailing closure syntax to work as expected:

let str2 = (createClass("someStr")["0"]){"One"}
print(str2)

Option 3: Add .self to the result before the trailing closure syntax:

That again finishes the subscripting call and avoids the confusion.

let str3 = createClass("someStr")["0"].self {"One"}
print(str3)

Personally, I would choose Option 1, because trailing closure syntax is unnecessary syntactic sugar that clearly is not working here.


Solving the Challenge

In the comments I asked:

I agree that the trailing closure syntax is most likely a bug that they could fix, but what I don't understand is why you insist on using trailing closure syntax here. What is so objectionable about wrapping the closure in () to make the call explicit even if it is just to work around a bug in Swift?

You replied:

the reason for the insisting is that I'm trying to solve a challenge.
actually , this function that return a closure is only one side of it
it goes like this

func Challenge() {
// Do not edit below this line
XCTAssertEqual(foo("str1")["str2"]{ "654321" }, "123456")
}

We've already established that trailing closure syntax is pairing the final closure with the indexing operation, so the trick is to design a class that takes a closure with its indexing operation:

class SomeClass {
subscript(_ s: String, closure: () -> String) -> String {
return String(closure().reversed())
}
}

func foo(_ str: String) -> SomeClass {
return SomeClass()
}

func Challenge() {
// Do not edit below this line
XCTAssertEqual(foo("str1")["str2"]{ "654321" }, "123456")
}

Reverse a string in Python

Using slicing:

>>> 'hello world'[::-1]
'dlrow olleh'

Slice notation takes the form [start:stop:step]. In this case, we omit the start and stop positions since we want the whole string. We also use step = -1, which means, "repeatedly step from right to left by 1 character".



Related Topics



Leave a reply



Submit