Search code examples
rusttraitsborrow-checker

How to implement Borrow<T> when T wraps borrowed data?


Given the following definitions from How to borrow a field for serialization but create it during deserialization?:

#[derive(Serialize)]
struct SerializeThing<'a> {
    small_header: (u64, u64, u64),
    big_body: &'a str,
}

#[derive(Deserialize)]
struct DeserializeThing {
    small_header: (u64, u64, u64),
    big_body: String,
}

How do I implement the Borrow trait so as to store the owned data naturally in (e.g.) HashMaps and query them by either them or by their borrowed counterparts? The closest thing that appears possible is as follows:

impl DeserializeThing {
    fn as_serialize(&self) -> SerializeThing<'_> {
        let DeserializeThing { small_header, big_body } = self;
        let small_header = *small_header;
        let big_body = big_body.as_str();
        SerializeThing { small_header, big_body }
    }
}

which is not quite sufficient.


Solution

  • You can't.

    Borrow::borrow must return a reference, and there is no way you can get a reference to a SerializeThing from a reference to a DeserializeThing as these types are simply not ABI compatible.


    If performance is important and you can't pay for the construction of DeserializeThing instances/allocation of strings, then you could use hashbrown::HashMap instead of std::collections::HashMap.

    hashbrown is the library that the standard library uses for its own HashMap implementation, but it has some more useful methods.

    One which would be useful for you now is the raw entry API (and mutable raw entry).

    In particular, it allows you to get a map entry from its hash and a matching function:

    pub fn from_hash<F>(self, hash: u64, is_match: F) -> Option<(&'a K, &'a > V)> where
       F: FnMut(&K) -> bool
    

    Access an entry by hash.

    Since you can implement Hash for both DeserializeThing and SerializeThing to get the same the hashes for the same values, this API would be simple to use in your case.