Moving out of a function both owned and borrowed entities?

Viewed 69

What is proper way of doing that? What options are available? I am aware about the problem of dangled references and the problem of self-referential struct. Still I have strong intuition it's a reachable problem because both owner and borrowed reference are returned and no memory deallocation happen. Also it's quite general problem! In theory, it's a solvable one.

use byte_slice_cast::*;

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

//

fn context_make<'a>() -> Context<'a> {
    Context::<'a>::new()
}

//

#[derive(Debug)]
struct Context<'a> {
    pub dst_buffer: Box<[f32]>,
    pub dst_buffer_bytes: &'a [u8],
}

//

impl<'a> Context<'a> {
    fn new() -> Context<'a> {
        let len: usize = 13;
        let dst_buffer: Box<[f32]> = vec![0_f32; len].into_boxed_slice();
        let dst_buffer_bytes = dst_buffer.as_byte_slice();
        Context {
            dst_buffer,
            dst_buffer_bytes,
        }
    }
}

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

Interesting to compare solutions if there are more than one alternatives.

Playground

2 Answers

You can not do this in safe rust. There are good reasons for this like not being able to trivially move the struct without changing where the reference points to.

Instead, you would implement a function to get that reference:

struct Context {
    pub dst_buffer: Box<[f32]>,
}

impl Context {
    fn new() -> Context {
        let len: usize = 13;

        Context {
            dst_buffer: vec![0_f32; len].into_boxed_slice(),
        }
    }

    fn dst_buffer_bytes(&self) -> &[u8] {
        self.dst_buffer.as_byte_slice()
    }
}

And if you really want to print out the bytes too:

use std::fmt;

impl fmt::Debug for Context {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Context")
         .field("dst_buffer", &self.dst_buffer)
         .field("dst_buffer_bytes", self.dst_buffer_bytes())
         .finish()
    }
}

Solution I have found. It possible with help of create owning-ref. Interesting is it possible to reach the same result with help of standard Pin?

use byte_slice_cast::*;
use owning_ref::*;

fn main() {
    let context = Context::new();
    dbg!(&context);
    dbg!(context.dst.as_owner());
    dbg!(&*context.dst);
}

//

#[derive(Debug)]
struct Context {
    // pub dst_buffer : Box::< [ f32 ] >,
    // pub dst_buffer_bytes : &'a [ u8 ],
    pub dst: OwningRef<Box<[f32]>, [u8]>,
}

//

impl Context {
    fn new() -> Context {
        let len: usize = 2;
        let dst_buffer: Box<[f32]> = vec![0_f32; len].into_boxed_slice();
        // let dst_buffer_bytes = dst_buffer.as_byte_slice();

        let dst = OwningRef::new(dst_buffer);
        let dst = dst.map(|dst_buffer| dst_buffer.as_byte_slice());

        Context { dst }
        // Context { dst_buffer, dst_buffer_bytes }
    }
}

Playground

Related