我正在编写一个玩具SQL数据库来学习Rust和关于数据库的知识(我正在翻译用C编写的教程中的代码)。由于借用检查器错误,我在编译以下代码时遇到了麻烦:
struct Pager {
pages: [Option<Box<[u8; PAGE_SIZE]>>; TABLE_MAX_PAGES]
}
impl Pager {
fn new() -> Self {
const INIT: Option<Box<[u8; PAGE_SIZE]>> = None;
Self {
pages: [INIT; TABLE_MAX_PAGES],
}
}
fn get_page(&mut self, page_num: usize) -> &mut [u8] {
// returns a page, creating a new one if necessary
let page = Box::new([0u8; PAGE_SIZE]);
self.pages[0].replace(page);
&mut self.pages[page_num].as_mut().unwrap()[..]
}
}
struct Table {
pager: Pager,
num_rows: usize,
}
impl Table {
fn new() -> Self {
Self { pager: Pager::new(), num_rows: 0 }
}
fn cursor_value(&mut self, cursor: &Cursor) -> &mut [u8] {
let row_num = cursor.row_num;
let page_num: usize = row_num / ROWS_PER_PAGE;
let byte_offset = (row_num % ROWS_PER_PAGE) * ROW_SIZE;
let page = self.pager.get_page(page_num);
&mut page[byte_offset..byte_offset+ROW_SIZE]
}
}
struct Cursor<'a> {
table: &'a Table,
row_num: usize,
end_of_table: bool,
}
impl <'a> Cursor<'a> {
fn table_end(table: &'a Table) -> Self {
Self { table, row_num: table.num_rows, end_of_table: true }
}
fn advance(&mut self) {
self.row_num += 1;
if self.row_num >= self.table.num_rows {
self.end_of_table = true;
}
}
}
fn execute_insert(table: &mut Table) {
let cursor = Cursor::table_end(&table);
let _buf = table.cursor_value(&cursor);
// here we insert new row into the table by writing into _buf
// ...
}
问题是我在execute_insert
函数中借用了两次表:一次使用可变引用(对于cursor_value
方法),一次使用不可变引用(对于table_end
方法):
28 | let cursor = Cursor::table_end(&table);
| ------ immutable borrow occurs here
29 | let buf = table.cursor_value(&cursor);
| ^^^^^^------------^^^^^^^^^
| | |
| | immutable borrow later used by call
| mutable borrow occurs here
在Rust中有什么方法可以实现这一点吗?或者我应该对这些数据结构使用不同的设计吗?
1条答案
按热度按时间tzxcd3kk1#
您的代码在概念上是不合理的,可能存在多个游标,如果一个游标执行
table.cursor_value(&cursor)
,那么另一个游标的self.table.num_rows
也会改变,end_of_table
可能为true,尽管游标不在末尾。这就是此代码无法编译的原因:Rust保证了只要你拥有一个不可变的引用(比如
Cursor
中的self.table
),那么它的值就不会改变。因此,只要你在Cursor
中不可变地引用它,并且没有通过Rc<RefCell>
或类似的方式使用内部可变性,你就永远不能修改表。解决办法对我来说很简单:你一次只需要一个游标(至少我是这么假设的),所以只要通过
&mut
引用self.table
,那么你就不需要Table::cursor_value()
了,你可以直接从Cursor::value()
开始。就像这样: