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 usingunsafe
. - 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
andframe_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,
});
}
}
}