Using LosslessStringConvertible initializer with a Substring parameter


I have a function I've written in Swift to parse a list of numbers from a Substring:

func parseNumbers<T> (_ line : Substring) -> [T] where T : LosslessStringConvertible, T : Numeric {

    return line.split(separator: " ").dropFirst().map() { T(String($0))! }


If I remove the String() call in the map closure, I get a compiler error (Xcode 12.2) saying Cannot convert value of type 'Substring.SubSequence' (aka 'Substring') to expected argument type 'String' even though, AFAICT, the requirement for LosslessStringConvertible's initializer is to take a StringProtocol, which Substring implements. However, if I change the above to be specific to Double instead of taking a type parameter, it works fine without the conversion to String. Just wondering what I might be misunderstanding about the protocols and/or generics. Thank you!


Where do you see that? LosslessStringConvertible has only one requirement, which is a failable initializer that takes a String:

Ah, thanks. It was my mistake, I was misreading this source code as relevant: swift/stdlib/public/core/FloatingPointParsing.swift.gyb

It seems to make sense to me have LosslessStringConvertible's functions take a StringProtocol, however, to avoid copies if callers can provide a Substring. Since String & Substring both implement the same interface it could be an easy change (I think).

In fact, it is impossible to make such a change, because it would break the ABI.

Thanks! I didn't realize it was ABI breaking to change an argument type to a protocol that the type implements.

Perhaps another initializer that takes StringProtocol would work, then. Being able to save a String construction & copy if the user who is calling into LosslessStringConvertible functions only has Substring (and, in fact, has taken care to use Substring to avoid String copies, such as what I did here) seems in the spirit of StringProtocol/String/Substring, and enough to justifying the process of deprecating the old one and providing a new one for callers to recompile with. Definitely not an urgent change by any means, though.

That, too, would break the ABI (unless it is given a default implementation that calls the current initializer, which would defeat the purpose).

Terms of Service

Privacy Policy

Cookie Policy