I've just started learning Rust, and I think I'm getting the hang of it, but I'm a little confused about when to use traits vs methods, especially with respect to the From and TryFrom traits (which is the one I've dealt with the most).
If I have a struct that contains some collection of data, and I want the ability to convert the struct into a Vec<u8>, is it better to have a method on the struct that returns the Vec<u8>, or to implement From<&MyStruct> for Vec<u8>
As a very simple example, here is a simple struct with both an method, and an implementation of From
From my understanding, both options will provide me with the same result (that is my_instance.to_bytes() == Vec::from(&my_instance). Is there a reason that I would want to implement From instead of just having a method that returns the Vec?
This question extends the other direction as well, should I create constructors, or should I be implementing From<Vec<u8>> for MyStruct?
I mean, you could argue that every function could be replaced with an appropriate From implementation. So, "is it possible" isn't a good basis.
Personally, my rules of thumb are:
Is this function conceptually a conversion? That is, does it take a concept represented by one type and turning it into the same concept, just represented with a different type? Implement From/Into.
Is it useful to provide a From/Into implementation? For example, do I expect this type to be used in contexts where that's needed (like for error types, or I expect to do frequent .map(Into::into) on iterators? Do I want to use it with generic methods that want those impls? Implement From/Into.
Also note that implementing From/Into does not preclude you from also implementing a method that does the same thing. Sometimes, that can be very handy if type inference might not be convenient. It's not uncommon to have both.
For example, if to_bytes is a common thing to want to do here, I'd definitely provide the method so that it's easy to invoke.
I'd only write the trait implementation From<_> for Vec<_> if there was an obvious relation between your type and a Vec<_> (like you contain a Vec<_> that would be useful on its own), or if there was some other concrete reason you had to meet an Into: Vec<_> bound or the like. If the relationship isn't obvious, a well-named method can elaborate. It's also fine to have method in addition to trait implementations.
I wouldn't write From<Vec<_>> unless it was similarly obvious; most likely because I have such a Vec<_> as a field and everything else is default-able or calculatable from the Vec<_>.
In the example, though I know it's contrived,
From<MyStruct> for Vec<u8>
You don't contain a Vec<_>, so why treat that container special
If the fields had more realistic names, they probably wouldn't have an implicit order anyway
From<Vec<u8>> for MyStruct
You don't contain a Vec<_>, so why treat that container special
The Vec<_> might have less than two elements and From shouldn't panic
The Vec<_> might have more than two elements you would be throwing out, so it's not a good fit that way either
For even [u8; 2] to make sense, again the order of fields would have to be inherent for it to be a good fit
Sorry for taking some time to reply. Both of your answers were very useful. I ended up using a mix of both depending on what I was doing. Mostly using my own methods, as that just felt more natural to me, but I did use TryFrom in a number of places for creating new instance.