Moving out of a function both owned and borrowed entities?

I stuck with moving out both owned and borrowed entities out of the function.

#![feature(new_uninit)]

use byte_slice_cast::*;
use std::io;

fn main() {
    let context = context_make();
    dbg!(&context);
}

//

fn context_make<'a>() -> Context<std::io::Cursor<&'a mut [u8]>> {
    Context::<std::io::Cursor<&mut [u8]>>::new()
}

//

#[derive(Debug)]
struct Context<W>
where
    W: std::io::Write + std::io::Seek,
{
    pub dst_cursor: W,
    pub dst_buffer: Box<[f32]>,
}

//

impl<W> Context<W>
where
    W: std::io::Write + std::io::Seek,
{
    fn new<'a>() -> Context<std::io::Cursor<&'a mut [u8]>> {
        let dst_buffer = Box::<[f32]>::new_zeroed_slice(1024);
        let mut dst_buffer = unsafe { dst_buffer.assume_init() };
        let dst_buffer_bytes = dst_buffer.as_mut_byte_slice();
        let dst_cursor = io::Cursor::new(dst_buffer_bytes);
        let context = Context {
            dst_buffer,
            dst_cursor,
        };
        context
    }
}

What is proper way of doing that?

Output:

error[E0505]: cannot move out of `dst_buffer` because it is borrowed
  --> src/problem.rs:44:29
   |
38 |   fn new< 'a >() -> Context< std::io::Cursor< &'a mut [ u8 ] > >
   |           -- lifetime `'a` defined here
...
42 |     let dst_buffer_bytes = dst_buffer.as_mut_byte_slice();
   |                            ------------------------------ borrow of `dst_buffer` occurs here
43 |     let dst_cursor = io::Cursor::new( dst_buffer_bytes );
44 |     let context = Context { dst_buffer, dst_cursor };
   |                             ^^^^^^^^^^ move out of `dst_buffer` occurs here
45 |     context
   |     ------- returning this value requires that `dst_buffer` is borrowed for `'a`

Is there a better way to call constructor new ( without writing helper context_make ).

Note: this code requires byte-slice-cast = "1.2.0"

Original plaground
Simplified playground
Related question

Looks like you're trying to build a self-referential struct, since dst_cursor needs a reference to dst_buffer.

2 Likes

That's right.

You can't make self-referential structs in safe Rust.

2 Likes

Probably that crate might help? But maybe there is solution without extra crates?

You could put the buffer directly into the Cursor instead of using a reference.

2 Likes

Both dst_buffer_bytes and dst_cursor have the same lifetime and are both bound to the object Context. I don't see how that might cause mem issues?

You could put the buffer directly into the Cursor instead of using a reference.

Hm.. In that case compiler complains:

error[E0515]: cannot return value referencing local variable `dst_buffer`
  --> src/problem.rs:47:5
   |
44 |     let dst_buffer_bytes = dst_buffer.as_mut_byte_slice();
   |                            ------------------------------ `dst_buffer` is borrowed here
...
47 |     context
   |     ^^^^^^^ returns a value referencing data owned by the current function

The Rust borrow checker does not allow moving objects that are borrowed, but moving your struct (e.g. when you return it from new) would move all of its fields.

There is a way to make it work - you just did it wrong in some way.

2 Likes

Thanks. Interesting how..

I took a stab at it, but you are using some as_mut_byte_slice that doesn't actually exist. Maybe you can explain what you're trying to do first?

1 Like

It is form crate byte-slice-cast = "1.2.0"

Apparently that's AsMutByteSlice in byte_slice_cast - Rust, converting &mut [f32] to &mut [u8].

The analogous operation on owned values would convert Box<[f32]> to Box<[u8]>, which - then again - seems a bit pointless; why not directly create the latter?

1 Like

Yes. It returning a structure with the cursor, but better with both fields original slice and the cursor.

If you move the struct around, then any pointer to its fields wouldn't be updated, and would be dangling.

1 Like

If you move the struct around, then any pointer to its fields wouldn't be updated, and would be dangling.

Can I make the struct owner of the data?

You can, that's not the point. The point is that you can't make a reference-typed field of a struct point to itself.

3 Likes

Because format is [ f32 ]. It is sound wave. But [ u8 ] is what Cursor expects.
2 representations of the same data.

You can, that's not the point. The point is that you can't make a reference-typed field of a struct point to itself.

My understanding is I move both enitities owner and borrowed Context { dst_cursor, dst_buffer } so no dangling. Perhaps there is some kind of marker to tell compiler about that?

Hmm, I think converting between Box<[f32]> and Box<[u8]> is not actually possible due to alignment differences. I guess you could create your own wrapper for Cursor that implements Write (and forwards the Seek implementation) anyways...

#![feature(new_uninit)]

use byte_slice_cast::*;
use std::{
    cmp,
    io::{self, Write},
};

fn main() {
    let context = context_make();
    dbg!(&context);
}

//

fn context_make() -> Context {
    Context::new()
}

//

#[derive(Debug)]
struct Context {
    pub dst_cursor: MyCursor,
}

//

impl Context {
    fn new() -> Context {
        let mut dst_buffer = vec![0_f32; 1024].into_boxed_slice();

        let dst_buffer_bytes = dst_buffer.as_mut_byte_slice();
        let dst_cursor = io::Cursor::new(dst_buffer_bytes);
        let context = Context {
            dst_cursor: MyCursor(io::Cursor::new(WrappedBox(dst_buffer))),
        };
        context
    }
}

#[derive(Debug)]
struct MyCursor(io::Cursor<WrappedBox>);
impl MyCursor {
    fn into_inner(self) -> Box<[f32]> {
        self.0.into_inner().0
    }
}

#[derive(Debug)]
struct WrappedBox(Box<[f32]>);
impl AsRef<[u8]> for WrappedBox {
    fn as_ref(&self) -> &[u8] {
        self.0.as_byte_slice()
    }
}
impl AsMut<[u8]> for WrappedBox {
    fn as_mut(&mut self) -> &mut [u8] {
        self.0.as_mut_byte_slice()
    }
}

// implementation adapted from standard library
impl io::Write for MyCursor {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let original_pos = self.0.position();
        let slice = self.0.get_mut().as_mut();
        let pos = cmp::min(original_pos, slice.len() as u64);
        let amt = (&mut slice[(pos as usize)..]).write(buf)?;
        self.0.set_position(original_pos + amt as u64);
        Ok(amt)
    }

    #[inline]
    fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
        let mut nwritten = 0;
        for buf in bufs {
            let n = self.write(buf)?;
            nwritten += n;
            if n < buf.len() {
                break;
            }
        }
        Ok(nwritten)
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

impl io::Seek for MyCursor {
    #[inline]
    fn seek(&mut self, style: io::SeekFrom) -> Result<u64, std::io::Error> {
        self.0.seek(style)
    }

    #[inline]
    fn stream_position(&mut self) -> io::Result<u64> {
        self.0.stream_position()
    }
}

(code compiles, but otherwise untested)

1 Like