Using pointer to slice from vec when calling C code from Rust

I'm converting code from C to Rust

C code:

uint16_t W25Q64_read(uint32_t addr,uint8_t *buf,uint16_t n){ 
  unsigned char *data;
  int rc;

  data = (unsigned char*)malloc(n+4);
  data[0] = CMD_READ_DATA;
  data[1] = (addr>>16) & 0xFF;     // A23-A16
  data[2] = (addr>>8) & 0xFF;      // A15-A08
  data[3] = addr & 0xFF;           // A07-A00
  rc = wiringPiSPIDataRW (_spich,data,n+4);
  return rc-4;

Rust code:

pub fn read(&self, address: u32, number_of_bytes: u16) ->  Result<Vec<u8>, u16> {
    let s: usize = number_of_bytes as usize + 4;
    let mut data = vec![0u8;s];
    data[0] = CMD_READ_UNIQUE_ID;
    data[1] = (address>>16 & 0xFF) as u8;     // A23-A16
    data[2] = (address>>8 & 0xFF) as u8;      // A15-A08
    data[3] = (address & 0xFF) as u8;         // A07-A00
    let mut _r: i32 = 0;
    _r = unsafe{wiringPiSPIDataRW(self.spi_channel,data.as_mut_slice().as_mut_ptr(), data.len() as i32)};
    let v = (&(data.as_slice())[4..]).to_vec();

On the C code, I get 256 255 results when I try to read 256 bytes. On the Rust code, for the same address and same number of bytes, I get


I always get this, I don't know why.

My assumption is that something is wrong with using a pointer to a slice owned by a vector. Somehow when C code tries to write to this slice, things go wrong. Could this possibly be the problem?

I cannot see anything wrong except for this.


1 Like

As an aside, you can simplify this assignment:

let v = (&(data.as_slice())[4..]).to_vec();

to this:

let v = data[4..].to_vec();

Similarly, you can call data.as_mut_ptr() directly, without calling .as_slice().


I reckon you'll be getting an error from the OS and reading garbage off the SPI bus, but don't notice because you never check the _r return code.

Your Rust code looks pretty much identical with the exception that the Rust version sends down a bunch of zeroes after the CMD_READ_DATA while C sends down uninitialized bytes. I'm assuming the other end never reads the message payload after the first 4 bytes though, so that shouldn't make a difference.

You are passing data pointer into wiringPi just fine, although data.as_mut_slice().as_mut_ptr() can be simplified to just data.as_mut_ptr() because Vec<u8> dereferences to a slice.

This is how I ported the C code. It's pretty similar to your version, except I added some error handling using std::io::Error's last_os_error() constructor.

const CMD_READ_DATA: u8 = 0x42;

pub fn W25Q64_read(channel: c_int, address: u32, number_of_bytes: usize) -> Result<Vec<u8>, Error> {
    let mut data = vec![0; number_of_bytes + 4];

    data[0] = CMD_READ_DATA;
    let address = address.to_le_bytes();

    unsafe {
        let bytes_written = wiringPiSPIDataRW(channel, data.as_mut_ptr(), data.len() as c_int);

        // wiringPiSPIDataRW() just does an ioctl call under the hood, so we
        // follow the usual Linux error handling conventions
        if bytes_written >= 0 {
            // throw away the first 4 bytes
            let _ = data.drain(..4);
            // And make sure we only return the number of bytes that was written
            data.truncate(bytes_written - 4);
        } else {