Safe way to convert Vec<u8> of len n*4 to a Vec<Rgba> of len n

I have the following struct:

pub struct Rgba {
    r: u8,
    g: u8,
    b: u8,
    a: u8,

I also have v: Vec<u8> of len n*4, guaranteed to be rgba format. Is there a way, without manually looping through every element, to "cast" this Vec<u8> of len n*4 to a Vec<rgba> of len n ?

Given that:

alloc::Layout::<[u8; 4 * N]>::new() == alloc::Layout::<[Rgba; N]>::new()

(because mem::size_of::<Rgba>() == 4 * mem::size_of::<u8>() AND because

mem::align_of::<Rgba>() == mem::align_of::<u8>()

(that last condition is paramount for the safety of such cast).

You can then define the following function:


fn transmute_vec (vec: Vec<u8>) -> Vec<Rgba>
    use ::std::{alloc, mem};

    const K: usize =
        mem::size_of::<Rgba>() / mem::size_of::<u8>()


    assert_eq!(vec.len() % K, 0);
    assert_eq!(vec.capacity() % K, 0);
    let (ptr, len, cap): (*mut u8, _, _) = vec.into_raw_parts();
    unsafe {
        // # Safety
        //  - It is safe to transmute between `[u8; K]` and `Rgba`,
        //      - (thanks to `Rgba` being a `#[repr(C)]` struct with `K` u8 fields)
        //   - `Layout::<[u8; K * N]>::new() == Layout::<[Rgba; N]>::new()`
            len / K,
            cap / K,

The above requires nightly dues to the vec_into_raw_parts feature. You can reimplement it in stable with the following code:

trait VecIntoRawParts {
    type Item;

    fn into_raw_parts (self: Self)
      -> (*mut Self::Item, usize, usize)

impl<T> VecIntoRawParts for Vec<T> {
    type Item = T;
    fn into_raw_parts (self: Self)
      -> (*mut Self::Item, usize, usize)
        let mut this = ::core::mem::ManuallyDrop::new(self);
        let length = this.len();
        let capacity = this.capacity();
        (this.as_mut_ptr(), length, capacity)

This can only be done safely if the capacity is a multiple of 4. Generally speaking that's not something you can guarantee unless you use Vec::with_capacity to create it.

Vec does not guarantee any particular growth strategy when reallocating when full, nor when reserve is called. The current strategy is basic and it may prove desirable to use a non-constant growth factor. Whatever strategy is used will of course guarantee O(1) amortized push .

Because rust's current expansion strategy is to double the capacity on overfill, you will seldom run into this issue, but it is still possible:

fn main() {
    let mut v = vec![1, 2, 3];
    println!("{}", v.capacity());  // Currently prints 6

My crate slice_of_array will allow you to safely convert slices of the vec: This is to say, any owned data you pass around must be Vec<u8>, but you can easily view it as &[Rgba] or &mut [Rgba] at any time.

use slice_of_array::prelude::*;

unsafe impl slice_of_array::IsSliceomorphic for Rgba {
    type Element = u8;
    const LEN: usize = 4;

fn main() {
    let v: Vec<u8> = vec![1, 2, 3, 255, 1, 2, 3, 255];

    let rgba: &[Rgba] = v.nest(); // panics if len not divisible by 4
    assert_eq!(rgba[0], Rgba { r: 1, g: 2, b: 3, a: 255 });

This is interesting. CAPACITY (as opposed to length) being multiple of 4 is not something I considered.

  1. I am reading data from a HTML Canvas Element:

  2. The ImageData has a data field, which is an

  3. This is then read into Rust/wasm32/stdweb via

  4. We get a vec via calling ".tovec" -- this definitely has length that is multiple of 4. As for capacity, I'm not sure.

In this case, capacity is equal to length, but only because the implementation of stdweb::TypedArray::to_vec uses Vec::with_capacity.

If you want to remain safe, you can do the following:

let slice: Box<[u8]> = vec.into();
let vec: Vec<u8> = slice.into();

The docs of Vec guarantee that this will set cap = len and will only reallocate if necessary. (oddly enough, there is still no such guarantee given for shrink_to_fit(), so stay away from that for now)

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.