Fail to create and return owned object and its borrowed item

Dear Rustaceans,

I'm having trouble to create and return owned object and its borrowed form.
Here's my code snippet.

use core::mem;

/// Existing library to use
#[derive(Debug)]
struct NativeBuf {
    buf: [u8],
}

impl NativeBuf {
    fn new(buf: &mut [u8]) -> &Self {
        unsafe {
            mem::transmute::<&[u8], &Self>(buf)
        }
    }
    // More APIs that uses bindgen for calling C libraries.
}


/// Start of my wrapper
#[derive(Debug)]
struct NativeBufHolder<'a> {
    native_buf: &'a NativeBuf,
    buf: Vec<u8>,
}

impl<'a> NativeBufHolder<'a> {
    fn new() -> Self {
        let mut buf = Vec::<u8>::with_capacity(50);
        buf.resize(50, 0);
        let native_buf = NativeBuf::new(buf.as_mut_slice());
        
        Self {
            native_buf: native_buf,
            buf: buf,
        }
    }
}

fn main() {
    let mut buf = Vec::<u8>::with_capacity(50);
    buf.resize(50, 0);
    println!("{:?}", NativeBuf::new(buf.as_mut_slice()));

    let holder = NativeBufHolder::new();
    println!("{:?}", holder);
}

I see following compilation error complaining that I can't return data owned by the current function.

   Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing local variable `buf`
  --> src/main.rs:32:9
   |
30 |           let native_buf = NativeBuf::new(buf.as_mut_slice());
   |                                           ------------------ `buf` is borrowed here
31 |           
32 | /         Self {
33 | |             native_buf: native_buf,
34 | |             buf: buf,
35 | |         }
   | |_________^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `buf` because it is borrowed
  --> src/main.rs:34:18
   |
26 |   impl<'a> NativeBufHolder<'a> {
   |        -- lifetime `'a` defined here
...
30 |           let native_buf = NativeBuf::new(buf.as_mut_slice());
   |                                           ------------------ borrow of `buf` occurs here
31 |           
32 | /         Self {
33 | |             native_buf: native_buf,
34 | |             buf: buf,
   | |                  ^^^ move out of `buf` occurs here
35 | |         }
   | |_________- returning this value requires that `buf` is borrowed for `'a`

Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
error: could not compile `playground` due to 2 previous errors

Since I return both owned object and its borrowed item altogether by moving,
I think that there could be a way such as creating internal buf with lifetime of NativeBufHolder.

Can someone recommend me a working solution?

I've also took a look at Box<[u8]> or Box<u8> but didn't went well. Here's another code snippet.

/// Start of my wrapper
#[derive(Debug)]
struct NativeBufHolder<'a> {
    native_buf: &'a NativeBuf,
    buf: Box<u8>,   // [u8] cause 'trait bounds were not satisfied` error.
}

impl<'a> NativeBufHolder<'a> {
    fn new() -> Self {
        let mut buf = Box::<u8>::new(50);
        let mut raw_buf = Box::into_raw(buf);
        let native_buf = NativeBuf::new(raw_buf);
        
        Self {
            native_buf: native_buf,
            buf: Box::<u8>::from_raw(raw_buf),
        }
    }
}

This cause expected &mut [u8], found *mut u8`

The reference to buf (which is just a memory address under the hood) will become invalid after buf is moved. The patern you're using is called self-referential struct and you'll find lots of material in this forum. I'm not sure about the alternatives, someone else probably knows how to help.

BTW, I think this is a good example of an error that Rust prevents. It is also UB in C.

If NativeBuf::new only consists of a transmute[1], there's no reason to store one for a long time.

Depending on the exact API you might even be able to implement Deref and DerefMut for NativeBufHolder. The API you included wouldn't let you do that part though.

use core::mem;

/// Existing library to use
#[derive(Debug)]
struct NativeBuf {
    buf: [u8],
}

impl NativeBuf {
    fn new(buf: &mut [u8]) -> &Self {
        unsafe { mem::transmute::<&[u8], &Self>(buf) }
    }
    // More APIs that uses bindgen for calling C libraries.
}

/// Start of my wrapper
#[derive(Debug)]
struct NativeBufHolder {
    buf: Vec<u8>,
}

impl NativeBufHolder {
    fn new() -> Self {
        let buf = vec![0u8; 45];

        Self { buf }
    }
}

impl NativeBufHolder {
    fn native_buf(&mut self) -> &NativeBuf {
        NativeBuf::new(self.buf.as_mut_slice())
    }
}

fn main() {
    let mut buf = vec![0u8; 50];
    println!("{:?}", NativeBuf::new(buf.as_mut_slice()));

    let mut holder = NativeBufHolder::new();
    println!("{:?}", holder.native_buf());
}

  1. or is similarly cheap ↩ī¸Ž

1 Like

Thank you for the reply and keyword.

However, I don't understand why reference to buf is invalidated after the move -- the moved one (i.e. returned object) is now new owner so I believe that the data itself should remain valid although previous buf object may be invalidated. Am I misunderstanding something?

Wow, it's different approach but looks good to me. Thanks,

In this specific case it would actually not be completely invalid because Vec stores it's data in a separate allocation, so the slice reference would still be pointing to the correct memory after the Vec value was moved. But lifetimes don't track that kind of information, and moves require that there are no borrows still being held to be allowed.

If you were using an array instead of a Vec it would be completely invalid since arrays don't manage a separate allocation.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.