Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can From trait implementations be lossy?

Context

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.

Question

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?

Related

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?

like image 495
Jeremy Avatar asked Jul 10 '18 17:07

Jeremy


1 Answers

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:

Lossless conversions

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)

Lossy conversions

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().

like image 178
Jeremy Avatar answered Oct 15 '22 10:10

Jeremy