0

I have Vec<u8> which I want to convert to (newtyped) Vec of (newtyped) arrays. I couldn't think of reason why I couldn't reuse existing allocation, so this is what I came up with:

use std::mem::{size_of, transmute, ManuallyDrop};

// If I understand correctly, size_of::<u8; N>() == N and
// repr(transparent) ensures that for newtyped [u8; N] too.
#[derive(Debug)]
#[repr(transparent)]
struct Foo([u8; 4]);

#[derive(Debug)]
struct FooVec(Vec<Foo>);

impl From<Vec<u8>> for FooVec {
    fn from(vec: Vec<u8>) -> Self {
        // Prevent contents of vec being dropped.
        let mut vec = ManuallyDrop::new(vec);

        // Compute length of foovec and ensure that there aren't any leftover bytes.
        let len = vec.len();
        assert!(len % size_of::<Foo>() == 0);
        let len = len / size_of::<Foo>();

        // Compute capacity of foovec and ensure that there aren't any leftover allocated bytes.
        let cap = if vec.capacity() % size_of::<Foo>() == 0 {
            vec.capacity() / size_of::<Foo>()
        } else {
            let cap = vec.capacity();
            let size = size_of::<Foo>();
            vec.reserve_exact(size - (cap % size));
            vec.capacity() / size
        };

        // Get pointer to contents of vec and transmute it to desired type.
        let ptr = unsafe { transmute::<*mut u8, *mut Foo>(vec.as_mut_ptr()) };

        // Construct foovec.
        unsafe { FooVec(Vec::from_raw_parts(ptr, len, cap)) }
    }
}

// Prints:
// [0, 1, 2, 3, 4, 5, 6, 7]
// FooVec([Foo([0, 1, 2, 3]), Foo([4, 5, 6, 7])])
fn main() {
    let vec: Vec<u8> = vec![0, 1, 2, 3, 4, 5, 6, 7];
    println!("{:?}", vec);

    let foovec = FooVec::from(vec);
    println!("{:?}", foovec);
}

Is this correct way to do this. Is there a safe way to do this?

awdrt
  • 23
  • 4
  • I guess it is the way to go. Except I would use `mem::forget` instead of `ManuallyDrop` and a simple pointer cast instead of `transmute`. – rodrigo Aug 18 '20 at 09:06
  • There are safe ways to do something *similar*, but transmuting a `Vec` is considered undefined behavior. To quote @Shepmaster *"In unsafe Rust, it is technically possible — you can shoot yourself in the foot as much as you'd like."* You can find more information in https://stackoverflow.com/questions/48308759/how-do-i-convert-a-vect-to-a-vecu-without-copying-the-vector. – Locke Aug 18 '20 at 11:36
  • @rodrigo Sure, there are valid uses for [`mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html#relationship-with-manuallydrop), but it would be great if you read the documentation before suggesting it, the link points to exact location, TL;DR of which is ***using mem::forget in here is undefined behaviour***. – Yamirui Aug 18 '20 at 12:28
  • The [duplicate applied to your case](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=23eb1b851dfd239ddfcf07c8a92a8f05) – Shepmaster Aug 18 '20 at 12:46
  • @Locke they're not transmuting a `Vec`, they're transmuting `vec.as_mut_ptr()`, which is fine I think. – Aloso Aug 18 '20 at 23:43
  • Thanks for answers. What's the policy of duplicate questions? Should I delete this or not? – awdrt Aug 19 '20 at 07:12
  • I just noticed that `Vec::shrink_to_fit`, `Vec::reserve_exact` etc. don't promise that capacity will be exactly the desired value. This seems problematic. Options would be panicking or leaking few bytes. Is there a way around this? – awdrt Aug 19 '20 at 08:27

0 Answers0