For some reason, this code fails with the following compile-time error:
error: cannot convert return expression of type 'T.SubSequence' to return type 'Substring'
This confused me because I thought that StringProtocol.SubSequence was just a type alias for Substring. Anyways, I thought "okay, let me check the definition of SubSequence on StringProtocol just to confirm my suspicions." Low and behold, I saw that in fact Substring is just the default value for StringProtocol.SubSequence and as such the two are not necessarily equal in a generic context. This surprised me, and I tried to think about why StringProtocol.SubSequence wouldn't just be a typealias for Substring because in practice, afaik, it always is, but to no avail. Then documentation I checked the documentation for StringProtocol and saw the following.
Do not declare new conformances to StringProtocol . Only the String and Substring types in the standard library are valid conforming types.
Since String.SubSequence is SubstringandSubstring.SubSequence is Substring, why would StringProtocol.SubSequence not just be equal to Substring? I literally cannot think of any reason/benefit for this. It seems to just be needlessly restrictive. Does anyone know why this is the case?
I noticed that changing the return type of foo to T.SubSequence fixes the problem above, but I also was a bit confused by that too. Shouldn't the return type be T.SubSequence.SubSequence.SubSequence.SubSequence.SubSequence instead? After some inspection, it seems that all of these operations return the SubSequence of the Collection they are called on. If this is the case and all of them produce the SubSequence of their input (even though in a non-generic context it is always Substring), why wouldn't the type be the longer aforementioned one? How does it know that all subsequent SubSequences of T are the same type? Can someone explain the black magic that is going on here?
Also, as a side note. A large amount of algorithms work with SubString as well as generic StringProtocol. So I do suggest that you use SubString instead, for convenience (and performance too, I believe).
Thanks @Lantua and @SDGGiesbrecht for your explanations. I had a feeling that it had something to do with legacy. Also, I totally forgot that Collection.SubSequence.SubSequence is equal to Collection.SubSequence, that totally makes sense then.
Anyways, I do like the suggestion of adding the extra requirement T.SubSequence == Substring on foo. That prevents a user from somehow passing in their own type that conforms to StringProtocol, yet for some reason has a SubSequence that is not Substring. I know no one would ever practically do this, but the restriction enforces the intended equality of the two types and fixes the problem.
Why not just work with T.SubSequence? Taking a quick look at StringProtocol, any conforming type's SubSequence must also conform the StringProtocol. So, unless you are using members that are strictly in Substring, you should be OK. Further, if the Swift team decides to go "screw it" and make more conforming types, your code won't choke.
You are generally correct. In my own experience, I have never actually had to write any code that mentions StringProcotol or Substring directly. Generic extensions to Collection and friends have always sufficed. The function discussed here could just as easily have been this instead:
But the original poster seemed to want the return type to be Substring for some reason, and the constraint I suggested expressed his apparent intention.
The provided code looks more like a concocted example than a verbatim use case, so I was trying to stick to what the poster actually asked for. Diverging from that runs the risk of the solution not being applicable to whatever the poster was actually trying to solve.