Reinterpret casting slices in Rust

When dealing with buffers in C, sometimes you see things like this:

void process_bytes(uint8_t* ptr)  
{
    int i;
    uint32_t* real_ptr = (uint32_t*)ptr;
    for (i = 0; i < PTR_SIZE; i++, real_ptr++)
    {
        *real_ptr = i + 4;
    }
}

We're reinterpret casting ptr into an array of uint32_t. In Rust terms, we want to reinterpret &mut [u8] into &mut [u32]. Since there's a dearth of recommended ways of approaching this, here's how you would do the same (at least, my way):

use std::slice;

fn process_bytes(ptr: &mut [u8]) -> Result<(), ()> {  
    // check that the length of ptr is some multiple of 4 bytes
    if ptr.len() % 4 != 0 { return Err(()) }

    // build a new slice struct by converting ptr to an actual ptr
    let mut real_ptr: &mut [u32] = unsafe {
        let r = slice::from_raw_parts_mut(ptr.as_mut_ptr() as *mut u32, ptr.len() / 4);
        drop(ptr);
        r
    };

    // iterate and assign
    for (i, x) in real_ptr.iter_mut().enumerate() {
        *x = i as u32 + 4;
    }

    // no errors here!
    Ok(())
}

Run this code in the Rust Playground.

This is naturally unsafe, and we have to be careful to avoid breaking invariants beyond the scope of the function. We avoid breaking the mutable alias invariant by dropping ptr; this prevents you from using ptr after real_ptr is defined, so we only have one active mutable reference to the same data. Try it by adding ptr.iter_mut(); somewhere after real_ptr, you'll get a use-after-move compile error. Obviously this is pedantic, but it's important to understand the implications of unsafe operations here.

Additionally, the representation of u32 depends on the endianness of the system the code is compiled for. You can see that the playground sandbox is little-endian! While this is a useful way of implementing certain algorithms that do not depend on the endianness of the data they work with, you should use the byteorder crate for situations where you are actually working with multi-byte numbers in a buffer.