rust

Initializing Rc<[u8]> directly without double allocation


I am trying to implement deserialization into Rc<[u8]> without double allocating. The solutions I saw always creates an intermediate Vec<u8> and filling it in, and then converting it to an Rc. But this causes double allocation, first when filling in the Vec<u8>, and second converting it into Rc<[u8]>.

Is there a way to directly allocate N bytes (N is read from the deserialization reader) into Rc<[u8]>, and using the buffer as input to the reader? I am OK to unsafe usage as long as there are no memory leaks.


Solution

  • This can be accomplished in safe code, by using Rc::from_iter with a TrustedLen iterator:

    Iterators of known length

    When your Iterator implements TrustedLen and is of an exact size, a single allocation will be made for the Rc<[T]>. For example:

    use std::rc::Rc;
    
    pub fn rc_slice<T: Default>(n: usize) -> Rc<[T]> {
        Rc::from_iter((0..n).map(|_| T::default()))
    }
    
    fn main() {
        let mut data = rc_slice::<u8>(16);
    
        // TODO: ovewrite contents of `data` with deserialized data
        *Rc::get_mut(&mut data).unwrap()[0] = ...;
    }
    

    Note: This solution relies on a zero/Default initialization of the Rc contents prior to deserialization, which works for u8. If this isn't available for your type, consider using unsafe to allocate an uninitialized Rc-slice.