英文:
How to solve multiple borrows compilation error
问题
在execute_insert
函数中,您尝试同时对table
进行可变引用和不可变引用,这会导致借用冲突。在Rust中,不允许同时拥有可变引用和不可变引用,因为这可能会导致数据不一致。要解决这个问题,您可以将table
的可变引用传递给table_end
,然后将它传递给cursor_value
。这样,您可以确保在同一时间只有一个可变引用。
以下是修改后的代码片段:
fn execute_insert(table: &mut Table) {
let mut cursor = Cursor::table_end(table); // 使用可变引用
let _buf = table.cursor_value(&mut cursor); // 传递可变引用给 cursor_value
// 在这里进行插入操作
}
通过将可变引用传递给table_end
和cursor_value
,您可以解决 borrow checker 错误。这确保了在同一时间只有一个可变引用,同时允许您进行插入操作。
英文:
I'm writing a toy SQL database to both learn Rust and about databases (I'm translating the code from a tutorial written in C). I'm having trouble getting the following code to compile, due to borrow checker errors:
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
// ...
}
The problem is that I'm borrowing the table twice inside the execute_insert
function: once with a mutable reference (for cursor_value
method) and once with an immutable one (for table_end
method):
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
Is there a way I can achieve this in Rust, or should I maybe use a different design for those data structures?
答案1
得分: 2
你的代码在概念上存在问题。可能存在多个光标,如果一个光标执行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()
中执行它。
像这样:
// 代码部分不翻译
英文:
Your code is conceptually unsound. It's possible that multiple cursors exist, and if one cursor does table.cursor_value(&cursor)
, then the self.table.num_rows
changes for the other cursor, and end_of_table
might be true, although the cursor is not at the end.
This is why this code fails to compile: It's guaranteed in Rust that as long as you hold an immutable reference to something (like self.table
in Cursor
), then its value will not change. Therefore you won't ever be able to modify the table as long as you reference it immutably in Cursor
and don't use inner mutability via Rc<RefCell>
or similar.
The solution is simple to me: you only want one cursor at a time anyway (that's at least my assumption), so simply reference self.table
via a &mut
reference. Then you don't need Table::cursor_value()
at all, you can directly do it from Cursor::value()
.
Like this:
const ROWS_PER_PAGE: usize = 64;
const ROW_SIZE: usize = 128;
const PAGE_SIZE: usize = ROW_SIZE * ROWS_PER_PAGE;
const TABLE_MAX_PAGES: usize = 32;
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 get_row(&mut self, row_num: usize) -> &mut [u8] {
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 mut Table,
row_num: usize,
end_of_table: bool,
}
impl<'a> Cursor<'a> {
fn table_end(table: &'a mut Table) -> Self {
let row_num = table.num_rows;
Self {
table,
row_num,
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 value(&mut self) -> &mut [u8] {
self.table.get_row(self.row_num)
}
}
fn execute_insert(table: &mut Table) {
let mut cursor = Cursor::table_end(table);
let _buf = cursor.value();
// here we insert new row into the table by writing into _buf
// ...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论