我正在尝试制作一个简单的分配器,从固定的缓冲池中分配和释放缓冲区。
struct AllocatedMemory<'a> {
mem: &'a mut [u8],
next: Option<&'a mut AllocatedMemory<'a>>,
}
struct Alloc<'a> {
glob: Option<&'a mut AllocatedMemory<'a>>,
}
impl<'a> Alloc<'a> {
fn alloc_cell(mut self: &mut Alloc<'a>) -> &mut AllocatedMemory<'a> {
let rest: Option<&'a mut AllocatedMemory<'a>>;
match self.glob {
Some(ref mut root_cell) => {
rest = std::mem::replace(&mut root_cell.next, None);
}
None => rest = None,
}
match std::mem::replace(&mut self.glob, rest) {
Some(mut root_cell) => {
return root_cell;
}
None => panic!("OOM"),
}
}
fn free_cell(mut self: &mut Alloc<'a>, mut val: &'a mut AllocatedMemory<'a>) {
match std::mem::replace(&mut self.glob, None) {
Some(mut x) => {
let discard = std::mem::replace(&mut val.next, Some(x));
let rest: Option<&'a mut AllocatedMemory<'a>>;
}
None => {}
}
self.glob = Some(val);
}
}
fn main() {
let mut buffer0: [u8; 1024] = [0; 1024];
let mut buffer1: [u8; 1024] = [0; 1024];
{
let mut cell1: AllocatedMemory = AllocatedMemory {
mem: &mut buffer1[0..1024],
next: None,
};
let mut cell0: AllocatedMemory = AllocatedMemory {
mem: &mut buffer0[0..1024],
next: None,
};
let mut allocator = Alloc { glob: None };
allocator.free_cell(&mut cell1); //populate allocator with a cell
allocator.free_cell(&mut cell0); //populate allocator with another cell (why does this fail?)
let mut x = allocator.alloc_cell();
allocator.free_cell(x);
let mut y = allocator.alloc_cell();
let mut z = allocator.alloc_cell();
allocator.free_cell(y);
allocator.free_cell(z);
}
}
错误为
error: `cell0` does not live long enough
allocator.free_cell(&mut cell0); //populate allocator with another cell (why does this fail?)
当我简单地删除cell0
并且只有cell1
可用于我的单元池时,就会发生以下错误:
error: allocator does not live long enough
let mut x = allocator.alloc_cell();
^~~~~~~~~
note: reference must be valid for the block suffix following statement 0 at 46:69...
next: None};
let mut cell0 : AllocatedMemory = AllocatedMemory{mem: &mut buffer0[0..1024],
next: None};
let mut allocator = Alloc {glob : None};
allocator.free_cell(&mut cell1); //populate allocator with a cell
//allocator.free_cell(&mut cell0); //populate allocator with another cell (why does this fail?)
note: ...but borrowed value is only valid for the block suffix following statement 2 at 49:48
let mut allocator = Alloc {glob : None};
allocator.free_cell(&mut cell1); //populate allocator with a cell
//allocator.free_cell(&mut cell0); //populate allocator with another cell (why does this fail?)
let mut x = allocator.alloc_cell();
allocator.free_cell(x);
...
error: aborting due to previous error
是否有人建议如何修复此代码,使其编译,并可能在免费列表中有2+项?
我想填充一个数组引用列表,然后能够弹出它们——使用它们一段时间,然后将使用过的/完成的值放回自由列表。
这里的动机是构建一个使用#![nostd]
指令的库,因此它需要一个分配器接口才能正常运行。
问题是您总是使用相同的生存期'a
。这迫使cell0
和cell1
具有相同的寿命,这是不可能的,因为必须首先定义一个。如果您仔细阅读错误消息,您可以看到它抱怨第二个单元格的生存期不包括定义第一个单元格的语句。
我不知道这是否是一个严格实现生存期的bug或错误特性,或者它是否是生存期类型系统中固有的(我还没有看到正式的定义)。
我也不知道如何解决它。我通过引入额外的生存期变量,在一些示例代码中解决了类似的问题,但我无法使其适用于您的代码。
多亏了starblue的指针,我决定通过将分配器和单元格放入一个结构并将该结构放入堆栈来强制它们的生存期相同。最终结果如下:
// This code is placed in the public domain
struct AllocatedMemory<'a> {
mem : &'a mut [u8],
next : Option<&'a mut AllocatedMemory <'a> >,
}
struct Alloc<'a> {
glob : Option<&'a mut AllocatedMemory <'a> >,
}
impl<'a> Alloc <'a> {
fn alloc_cell(self : &mut Alloc<'a>) -> &'a mut AllocatedMemory<'a> {
match self.glob {
Some(ref mut glob_next) => {
let rest : Option<&'a mut AllocatedMemory <'a> >;
match glob_next.next {
Some(ref mut root_cell) => {
rest = std::mem::replace(&mut root_cell.next, None);
},
None => rest = None,
}
match std::mem::replace(&mut glob_next.next, rest) {
Some(mut root_cell) =>
{
return root_cell;
},
None => panic!("OOM"),
}
},
None => panic!("Allocator not initialized"),
}
}
fn free_cell(self : &mut Alloc<'a>,
mut val : & 'a mut AllocatedMemory<'a>) {
match self.glob {
Some(ref mut glob_next) => {
match std::mem::replace(&mut glob_next.next ,None) {
Some(mut x) => {
let _discard = std::mem::replace(&mut val.next, Some(x));
},
None => {},
}
glob_next.next = Some(val);
},
None => panic!("Allocator not initialized"),
}
}
}
struct AllocatorGlobalState<'a>{
cell1 : AllocatedMemory<'a>,
cell0 : AllocatedMemory<'a>,
sentinel : AllocatedMemory<'a>,
allocator :Alloc<'a>,
}
fn main() {
let mut buffer0 : [u8; 1024] = [0; 1024];
let mut buffer1 : [u8; 1024] = [0; 1024];
let mut sentinel_buffer : [u8; 1] = [0];
let mut ags : AllocatorGlobalState = AllocatorGlobalState {
cell1 : AllocatedMemory{mem: &mut buffer1[0..1024],
next: None},
cell0 : AllocatedMemory{mem: &mut buffer0[0..1024],
next: None},
sentinel : AllocatedMemory{mem: &mut sentinel_buffer[0..1], next: None},
allocator : Alloc {glob : None},
};
ags.allocator.glob = Some(&mut ags.sentinel);
ags.allocator.free_cell(&mut ags.cell1);
ags.allocator.free_cell(&mut ags.cell0);
{
let mut x = ags.allocator.alloc_cell();
x.mem[0] = 4;
let mut y = ags.allocator.alloc_cell();
y.mem[0] = 4;
ags.allocator.free_cell(y);
let mut z = ags.allocator.alloc_cell();
z.mem[0] = 8;
//y.mem[0] = 5; // <-- this is an error (use after free)
}
}
我需要添加sentinel结构,以避免在进行多次分配时重复借用ags.allocater。
cell.rs:65:19: 65:32 help: run `rustc --explain E0499` to see a detailed explanation
cell.rs:62:19: 62:32 note: previous borrow of `ags.allocator` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `ags.allocator` until the borrow ends
由于sentinel存储在Alloc中,我可以保证在函数返回后永远不会修改glob
。