Home > Mobile >  Rust: How to create a mutable reference to a struct's member from another member during Struct:
Rust: How to create a mutable reference to a struct's member from another member during Struct:

Time:12-12

I'm trying to understand how I can attach pointers to objects (frames) which are allocated during Foo::new(), to some other objects (frame_descriptors) in the same block.

It seems like there are issues both with using the frames variable twice, and with returning the frames. I'm not sure I completely follow what the problem is/how to resolve it.

Would anyone be willing to offer a short explanation or guidance? I'd be very grateful.

const PAGE_SIZE: usize = 4096;
const NUM_FRAMES: usize = 1000;

pub struct Frame {
    pub data: [u8; PAGE_SIZE],
}

impl Default for Frame {
    fn default() -> Self {
        Self { data: [0; PAGE_SIZE] }
    }
}

pub struct FrameDescriptor<'bp> {
    pub frame: &'bp mut Frame,
    pub is_dirty: bool,
}

impl Default for FrameDescriptor<'_> {
    fn default() -> Self {
        let frame_ptr: *mut Frame = std::ptr::null_mut();
        let frame_ref: &mut Frame = unsafe { &mut *frame_ptr };
        Self { frame: frame_ref, is_dirty: false }
    }
}

pub struct BufferPool<'bp> {
    pub frames: Box<[Frame; NUM_FRAMES]>,
    pub frame_descriptors: Box<[FrameDescriptor<'bp>; NUM_FRAMES]>,
}

// ----> ISSUE OCCURS HERE, IN NEW <-----
impl BufferPool<'_> {
    pub fn new() -> Self {
        let mut frames: Box<[Frame; NUM_FRAMES]> =
            Box::new(core::array::from_fn(|_| Default::default()));

        let mut frame_descriptors: Box<[FrameDescriptor; NUM_FRAMES]> =
            Box::new(core::array::from_fn(|_| Default::default()));

        for i in 0..NUM_FRAMES {
            frame_descriptors[i].frame = &mut frames[i];
        }

        Self { frames, frame_descriptors }
    }
}

Here are the compiler errors on Nightly Rust, as of 12/11/2022:

error[E0499]: cannot borrow `frames[_]` as mutable more than once at a time
   --> src/main.rs:197:42
    |
189 |       pub fn new() -> Self {
    |                       ---- return type is BufferPool<'1>
...
197 |               frame_descriptors[i].frame = &mut frames[i];
    |                                            ^^^^^^^^^^^^^^ `frames[_]` was mutably borrowed here in the previous iteration of the loop
...
200 | /         Self {
201 | |             frames,
202 | |             frame_descriptors,
203 | |             free_list: (0..BUF_POOL_NUM_FRAMES).collect(),
...   |
206 | |             clock_hand: 0,
207 | |         }
    | |_________- returning this value requires that `frames[_]` is borrowed for `'1`

error[E0515]: cannot return value referencing local data `frames[_]`
   --> src/main.rs:200:9
    |
197 |               frame_descriptors[i].frame = &mut frames[i];
    |                                            -------------- `frames[_]` is borrowed here
...
200 | /         Self {
201 | |             frames,
202 | |             frame_descriptors,
203 | |             free_list: (0..BUF_POOL_NUM_FRAMES).collect(),
...   |
206 | |             clock_hand: 0,
207 | |         }
    | |_________^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `frames` because it is borrowed
   --> src/main.rs:201:13
    |
189 |       pub fn new() -> Self {
    |                       ---- return type is BufferPool<'1>
...
197 |               frame_descriptors[i].frame = &mut frames[i];
    |                                            -------------- borrow of `frames[_]` occurs here
...
200 | /         Self {
201 | |             frames,
    | |             ^^^^^^ move out of `frames` occurs here
202 | |             frame_descriptors,
203 | |             free_list: (0..BUF_POOL_NUM_FRAMES).collect(),
...   |
206 | |             clock_hand: 0,
207 | |         }
    | |_________- returning this value requires that `frames[_]` is borrowed for `'1`

CodePudding user response:

The simple answer is: You can't. You need to rework your object structure.

In Rust, every object is trivially movable via memcpy. That means, it's impossible to create self-referential data structures, as they can't be moved without creating dangling pointers, which is undefined behaviour. Rust has a zero-undefined-behaviour tolerance.

That said, there is ways around it. Let me give you some feedback for your code:

  • Never use unsafe to create invalid references. This is called unsound. Please read this article about soundness before using unsafe.
  • The idea of storing a nullpointer in a reference is probably ported over from C/C . Rust has the Option type for that instead.
  • Ask yourself: Why do you need the reference in the first place? In your case, I don't think it adds any information. The frames and frame_descriptors have an identical count, so it would be easy to match them through indices.

For more information, see Why can't I store a value and a reference to that value in the same struct?.

While I usually don't link against external references, this Youtube video shows the problem very in-depth and hands on. I highly recommend it.

CodePudding user response:

Here's a working solution if you're alright with it not being during ::new() and are okay with using a Option to represent "initialized or not"

(This technically still does not answer the question, which I think should be achievable through some usage of Pin<> and advanced techniques, so I will not mark it as the answer)

#[derive(Copy, Clone)]
struct Frame {
    data: [u8; 4096],
}

#[derive(Copy, Clone)]
struct FrameRef<'pool> {
    frame: &'pool Frame,
    dirty: bool,
}

struct Pool<'pool> {
    frames: Box<[Frame; 100]>,
    refs: Box<[Option<FrameRef<'pool>>; 100]>,
}

impl<'pool> Pool<'pool> {
    fn new() -> Pool<'static> {
        Pool {
            frames: Box::new([Frame { data: [0; 4096] }; 100]),
            refs: Box::new([None; 100]),
        }
    }

    fn init(&'pool mut self) {
        for i in 0..100 {
            self.refs[i] = Some(FrameRef {
                frame: &self.frames[i],
                dirty: false,
            });
        }
    }
}
  • Related