I have a pair of related structs in my program, Rom
and ProfiledRom
. They both store a list of u8
values and implement a common trait, GetRom
, to provide access to those values.
trait GetRom {
fn get(&self, index: usize) -> u8;
}
The difference is that Rom
just wraps a simple Vec<u8>
, but ProfiledRom
wraps each byte in a ProfiledByte
type that counts the number of times it is returned by get
.
struct Rom(Vec<u8>);
struct ProfiledRom(Vec<ProfiledByte>);
struct ProfiledByte {
value: u8;
get_count: u32;
};
Much of my program operates on trait GetRom
values, so I can substitute in Rom
or ProfiledRom
type/value depending on whether I want profiling to occur.
I have implemented From<Rom> for ProfiledRom
, because converting a Rom
to a ProfiledRom
just involves wrapping each byte in a new ProfiledByte
: a simple and lossless operation.
However, I'm not sure whether it's appropriate to implement From<ProfiledRom> for Rom
, because ProfiledRom
contains information (the get counts) that can't be represented in a Rom
. If you did a round-trip conversion, these values would be lost/reset.
Is it appropriate to implement the From
trait when only parts of the source object will be used?
I have seen that the standard library doesn't implement integer conversions like From<i64> for i32
because these could result in bytes being truncated/lost. However, that seems like a somewhat distinct case from what we have here.
With the potentially-truncating integer conversion, you would need to inspect the original i64
to know whether it would be converted appropriately. If you didn't, the behaviour or your code could change unexpectedly when you get an out-of-bounds value. However, in our case above, it's always statically clear what data is being preserved and what data is being lost. The conversion's behaviour won't suddenly change. It should be safer, but is it an appropriate use of the From
trait?
From
implementations are usually lossless, but there is currently no strict requirement that they be.
The ongoing discussion at rust-lang/rfcs#2484 is related. Some possibilities include adding a FromLossy
trait and more exactly prescribing the behaviour of From
. We'll have to see where that goes.
For consideration, here are some Target::from(Source)
implementations in the standard library:
Each Source
value is converted into a distinct Target
value.
u16::from(u8)
, i16::from(u8)
and other conversions to strictly-larger integer types.
Vec<u8>::from(String)
Vec<T>::from(BinaryHeap<T>)
OsString::from(String)
char::from(u8)
Multiple Source
values may be convert into the same Target
value.
BinaryHeap<T>::from(Vec<T>)
loses the order of elements.Box<[T]>::from(Vec<T>)
and Box<str>::from(String)
lose any excess capacity.
Vec<T>::from(VecDeque<T>)
loses the internal split of elements exposed by .as_slices()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With