String questions

1. Why is it illegal in Swift to write a substring expression using integers, i.e. string[1..<10]?


I understand that the length in bytes of a character varies according to the underlying character encoding, but shouldn't Swift be able to figure this out and produce the right result?


2. Why does a substring operation return a Substring object instead of a String object?


What is the use of a Substring object? In other words, in what scenario would one not immediately convert it to a String?


Both of these problems seem like a pretty big step backwards from every other language I have ever used (Objective-C, Java, C# and many others).


Thanks,

Frank

Accepted Reply

For 1.


It is easy to get an extension to do this:


extension String {  
     subscript (range: Range) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: range.lowerBound)
        let endIndex = self.index(self.startIndex, offsetBy: range.upperBound)
        return String(self[startIndex..<endindex])
    }
}

Credit: h ttps://edwardvalentini.com/2017/11/11/making-strings-integer-subscriptable/


2. Why does a substring operation return a Substring object instead of a String object?

What is the use of a Substring object? In other words, in what scenario would one not immediately convert it to a String?


That is clearly a design choice in Swift, that was (and is still if I understand well) discussed.

The interest of this is that you refer with the same index for the same part in String and its substring (substring is a 'screenshot window' in the String.


For both questions, this is an interesting discussion that you probably read already.

https://stackoverflow.com/questions/39677330/how-does-string-substring-work-in-swift

Replies

For 1.


It is easy to get an extension to do this:


extension String {  
     subscript (range: Range) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: range.lowerBound)
        let endIndex = self.index(self.startIndex, offsetBy: range.upperBound)
        return String(self[startIndex..<endindex])
    }
}

Credit: h ttps://edwardvalentini.com/2017/11/11/making-strings-integer-subscriptable/


2. Why does a substring operation return a Substring object instead of a String object?

What is the use of a Substring object? In other words, in what scenario would one not immediately convert it to a String?


That is clearly a design choice in Swift, that was (and is still if I understand well) discussed.

The interest of this is that you refer with the same index for the same part in String and its substring (substring is a 'screenshot window' in the String.


For both questions, this is an interesting discussion that you probably read already.

https://stackoverflow.com/questions/39677330/how-does-string-substring-work-in-swift

Thanks for the notes, and the link.


If nothing else, I'm disappointed about the fact that Apple saw fit to release something as a finished product but then make major breaking changes to it every year for four years in a row.


I worked as a C and C++ developer for more than 20 years, and never in that time (or since, as far as I know) has anyone changed the languages in a way that breaks older code. Yes there's something to be said for making improvements even at the expense of compatibility, but I'm not really sure what the point was of rushing an unfinished product out the door instead of waiting a couple of years to get it right. It's not as though we didn't have a way to write apps before 2014.


Alloc init,

Frank

1. Why is it illegal in Swift to write a substring expression using integers, i.e. string[1..<10]?


There is no simple Int-based subscripting for String because in Swift, by design, String is a Collection, and types conforming to Collection are required to make performance *guarantees* for many of their methods.


Subscripting, for *all* types conforming to Collection, must be constant-time — it should take about the same length of time to look up any element, and the lookup mustn't depend on the number of elements in the collection. String can't provide this guarantee for Int indexes without a lookup table (from Int indexes to character positions, basically), and that would dramatically increase the memory used, as well as slowing down operations that change a string's contents.


There is currently a proposal that will allow Int-offset-based subscripting with a more verbose structure. This is a compromise to provide a better syntax, but which avoids implying random access. If this proposal is accepted (it probably will be), then your example would be written string[.start + 1 ..< .start + 10]. You can also offset relative to the end of the string, too.


2. Why does a substring operation return a Substring object instead of a String object?


Claude already gave you one of the answers (because substrings use the same String.Index values as the underlying String). The other reason is that it's too expensive to create a separate String for all substring operations, because that would require part of the underlying data to be copied. That would likely cause memory allocations as well as copying overhead. Substrings share the underlying string storage, so its trivially cheap to construct a Substring.


If you want to create a separate string, you can easily do that by writing String(someSubstring), but in many use cases you won't need to do that.


>>Apple saw fit to release something as a finished product but then make major breaking changes to it every year for four years in a row


I really wish people wouldn't say things like this. It's not really true. Apple never presented Swift as a "finished product", and told developers it would change. In most cases, the year-by-year changes were accompanied by converters that updated your code for you.


It is true that for several years in a row (until Swift 4), Apple made changes that required developers to spend time converting their code every year, and that was certainly painful, just not as dire as you suggest. FWIW, the bar for source-breaking changes in Swift is currently *very* high, in some cases to the detriment of the language's future.


>>I worked as a C and C++ developer for more than 20 years, and never in that time (or since, as far as I know) has anyone changed the languages in a way that breaks older code


C has been around since the 1970s, and *of course* the C standard has changed in source-breaking ways in the last 20 years. It hasn't changed *much*, because the churn and standardization in the language happened *more than* 20 years ago, when it was new. Kinda like Swift is now. C++ has been around since the late 1980s, and there was lot of change there too, also more than 20 years ago.

I personally tried to put off working with Swift as long as I could, but many things pressured me into using it early, including market forces (non-technical customers specifically asking for their apps to be written in Swift, knowing only Apple's marketing information) plus having to work on apps that had been started in Swift by other developers. I am glad it is somewhat stable now, but I'm still disappointed in how quickly Apple has moved away from supporting "old" versions of Swift. This week I had occasion to do some maintenance on an app I wrote less than three years ago, and when I opened it, I was told that not only was Swift 3 out of date, but it could no longer even be converted to Swift 5 by Xcode.


As for the sting situation, the solution is pretty easy: have an index that is lazily initialized, so it doesn't take up space unless we need it.


Frank

>how quickly Apple has moved away from supporting "old" versions of Swift.


With the tools, sure hoop-jumping rules and I guess I get that, seeing the march towards maturity had to be gotten over with. Otherwise, when run, Swift is pretty backwards compatible I think, so it's not all bad news.