How to impl AsRef<[u8]> for u64

Hello, I recently try to use the crate sled which is a database. The database (Db) struct take a AsRef<[u8]> for keys and Into<IVec> for values. I implemented From<Value> for IVec by serialize Value into the RON format and convert the output string into &[u8].
I could use the same technique for my Key struct, but since it just a u64 wrapped, I would like to convert it into a &[u8] directly. Unfortunately, methods as to_be_bytes() return an [u8; 8] and when I try to convert it into an &[u8] the borrow checker tell me that I "cannot return reference to temporary value" because I "returns a reference to data owned by the current function".

Is there a way to do it ?

Here is my code :

use serde::Serialize;
use ron;

// Work
#[derive(Debug, Serialize)]
struct Value {
    post: Option<String>,
    tag: Option<String>,
    run: bool,
    update_post: bool,
}
impl From<Value > for IVec {
    fn from(value: Value ) -> Self {
        ron::ser::to_string(&value).unwrap().as_bytes().into()
    }
}

// Don't work 
struct Key(u64);

impl AsRef<[u8]> for Key {
    fn as_ref(&self) -> &[u8] {
        &self.0.to_be_bytes()
    }
}

Error:

cannot return reference to temporary value
returns a reference to data owned by the current function

You probably want u64::as_ne_bytes, but unfortunately that's not stable yet. You could implement it yourself using unsafe code, though:

// required for soundness!
#[repr(transparent)]
struct Key(u64);

impl AsRef<[u8]> for Key {
    fn as_ref(&self) -> &[u8] {
        let ptr = self as *const Self as *const u8;
        // SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
        // with alignment greater than that of u8
        unsafe { std::slice::from_raw_parts(ptr, 8) }
    }
}

If you do it this way then the serialized form of the Key will depend on whether the target architecture is big-endian or little-endian, which may or may not be a problem.

Thanks a lot, it's work perfectly !

I don't think having native endian on databases is a good idea. Instead, you can comvert the u64 into [u8; 8] using .to_be_bytes() within functions like Key::new() and store it internally.

1 Like

If you want sled's iterators to return data in order you need to use big-endian keys and the native endianness of all commonly used architectures is little-endian. So using native endianness might not be a good idea. I would suggest just returning the be key by value:

pub struct Key(u64);

impl Key {
    pub fn get_be_key(&self) -> [u8;8] {
        self.0.to_be_bytes()
    }
}

This is not slow.

1 Like

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.