indeed. swift solution seems a reasonable compromise, but not a silver bullet. example that would've benefited from "Substring semantics baked into String":
var s = "very long string here ..."
while s != "" {
s.removeLast()
do_something(s)
}
with "substring semantics" this loop could have run without memory allocation / string copying, just adjusting the length leaving the same base of the string. ditto for the head removal or both ends removal.
if substring semantics was baked into the String itself (so there was no need for a separate Substring type) i'd expect to see some "compact" API to convert String to its trimmed form:
s.removeLast(), etc
s.compact() // opt-in on an as needed basis
if i was designing the thing i'd probably go with this latter approach: just one small API method to surface (and for the users to know about when to use and when to not) instead of the heavier Substring / StringProtocol approach.
That's a fair preference. But I'm not convinced that this approach wouldn't be less optimal in practice. It would be easy, especially for newcomers to forget the .compact() calls, or it could quickly become noisy to include them in everywhere to avoid memory leaks.
But I guess it boils down to different trade-offs.
It does if you use actual slicing operations (dropLast() over removeLast()), or operate on a slice (var s = "string"[...]). And that works for all collections.
isn't the situation irt memory leaks and noise exactly the same now with Substring? extra noise due to "String(substring)" here and there, and this warning in the documentation which is very easy to miss / forget for newcomers:
IME, I often find heavy string manipulation code (one that warrants Substring) and other, lighter string usages to be separated relatively cleanly. Meaning that I'd only need to do String(substring) or .compact only when crossing between those two areas. In that regard, I do enjoy having type-level information to remind me to trim unused string portions.