Where in the conversion landscape does TryFrom live?

On a scale between "trivial types" and "very complex data sets", where does TryFrom live?

Whenever I see uses of TryFrom it's to report that an destination integer type can not hold a source integer value and such "trivial" cases.

But let's say you have a file format represented by a type, and this file format can be converted to another format. Would it be considered controversial to implement/express this conversion using TryFrom<FileFormatV1> for FileFormatV2?

I realize it technically can be used for as complex conversions as one wants, but I'm asking from the perspective of Principle of Least Astonishment.

I started making a pretty complicated TryFrom implementation and I realized I was POLA:ing myself, but I'm not sure if complex TryFrom implementations really should be stigmatized or not.

I'd guess it's a matter of taste. But then again, since you can actually return detailed error types using TryFrom, it is a reasonable thing to implement.

From- and Into-style conversions are not expected to be trivial by means of the idiomatic naming convention. I'm not sure what exactly you mean by converting between file formats, though – to me, that would imply creating a new file from an original. I don't see how that could be done through TryFrom.

My own take: it's Ok to have arbitrarily complex TryFrom as long as it's easy to explain why it may succeed or fail.

For example one can imagine TryFrom which creates representation of DAG from arbitrary graph.

It would, probably, be complex but if it only fails when graph is not DAG then it's easy to understand what happens on the conceptual level.

Now, imagine very simple TryFrom which converts Rc<Graph> into Graph if reference count shows that you are the only owner.

Technically it's valid TryFrom but I wouldn't implement it as TryFrom because it's failure or success is not, really, related to properties of Graph but to the ownership situation around that Graph in a given moment.

That's kinda bizzare and not obvious because it's not obvious what should happen if reference_count is 2. Would it fail? Would it succeed (via clone)? What about weak references? Do they affect the outcome or not?

You need to read the documentation and at this point it stops being useful to use familiar trait name.

My heuristic: Only have impl From<A> for B if it's reasonable to have an impl TryFrom<B> for A that can recover the original.

That vaguely implies a bunch of things, like successful conversion shouldn't be lossy, it should be the only reasonable way to do a conversion, etc.

Note that std doesn't always respect this. I'd have rathered that impl From<Vec<T>> for BinaryHeap<T> would have been BinaryHeap::heapify or something, since the original Vec cannot be recovered, even fallibly, since the original order is lost. (And it needing a comment on the impl is good evidence that its behaviour is not necessarily "obvious" enough to be a good From.)

So if you're starting to think it's "pretty complicated", I do think that's a good opportunity to see whether you can rearrange things somehow.

What's complicated about it? Could some of that complexity be handled by an infallible impl From<FileFormatV1> for SupersetFileFormat, maybe, in such a way that impl TryFrom<SuperSetFileFormat> for FileFormatV2 would be straight-forward enough to be unsurprising? (Just spitballing, since I have no idea about the details.)

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.