When to make a type LosslessStringConvertible

I'm designing an API for a type that seems like a good fit for LosslessStringConvertible - it represents a database connection string containing information about the hosts to connect to, configuration options, and so on, and given an instance of this type it is possible to construct a lossless string representation of it.

However, one concern I have with conforming to the protocol is the required failable initializer, which provides no way for me to tell the user what the problem with the input was - and there are a number of different things that could be wrong. In general I tend to only use failable inits when the failure case is really obvious.

Some things I've considered...

  • Maybe this means my type is not a good fit for such a protocol after all? Would the protocol actually provide that much value to users beyond what it signals about the type having a lossless string representation?
  • Maybe I could work around this by providing a second throwing initializer which throws a more descriptive error, and use some parameter label to make it unambiguous from the failable init, and users who want specific error info can use that one?

Curious to hear any thoughts on this :slightly_smiling_face:

1 Like

I was thinking about this recently, as well (for URLs and IP addresses, which can also be losslessly converted to/from String).

As I see it, any conforming type could fail to parse the String for any number of reasons, which in the context of LSC, all boil down to "this isn't the String I gave you". The condition for being losslessly convertible is that the type can always parse the serialized representation it gives, so all failures must come from somebody manipulating the String (as opposed to, say, some external state). IMO, the benefit to LSC is that it can be used in a generic context, where the caller couldn't possibly handle a specific parser error, anyway.

My advice would be to add a second, throwing initializer. For my types, I've called this init(reportingErrors: String) throws.

1 Like

I would say that it might not be a good fit. Inherent to LosslessStringConvertible is that, as the protocol says, “it should be possible to create an instance from its string representation,” or as @Karl put it more eloquently,

Frankly I’m not sure of the benefit of conforming a database connection type to this protocol. Put another way, is there a use case you have in mind for an algorithm that’s usefully generic over integers, strings, and database connection instances?

I like the reportingErrors suggestion. I'd thought of throwingIfInvalid but it felt too verbose. :slightly_smiling_face:

Given a string that was generated from an instance of this type, initialization from that string will always succeed. But a user could easily call the initializer with some string they created/edited themselves and it would fail.

To clarify this is not a connection type, it's a connection string, not unlike e.g. a URL. That said I agree I'm not totally sure what benefits this conformance provides beyond signaling information about the type.

Gotcha. That helps to clarify things; your type would seem to align nicely with the semantics of the protocol then. I would agree with @Karl’s suggestion of another initializer for users who want to know more about any errors. If the required initializer is an attractive nuisance, though, then there’s really no need to provide the conformance unless you have a generic use case in mind for it.

2 Likes

I see LSC as a sort of light-weight version of Codable. It provides a nice semantic guarantee to people who are, say, sending a value over an XPC connection. I could imagine a situation where a connection/monitoring UI occupies one process, and sends the connection info over to a networking process, and the scaffolding which enables that could use LosslessStringConvertible.

Another reason to conform is that it's a standard library protocol, so nobody but you (the owner of the custom type) can safely add that conformance. If you're already implementing a parser, and you already make sure that serialisation and re-parsing is idempotent, the cost of adding explicit LSC conformance is relatively low.

1 Like