rust 如何解决多次借用编译错误

xvw2m8pv  于 2023-03-12  发布在  其他
关注(0)|答案(1)|浏览(251)

我正在编写一个玩具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中有什么方法可以实现这一点吗?或者我应该对这些数据结构使用不同的设计吗?

tzxcd3kk

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()开始。
就像这样:

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
    // ...
}

相关问题