此 Rust 代码会导致未定义的行为吗?

问题描述 投票:0回答:1

到目前为止,我只用 Rust 编写了安全代码。现在我正在从头开始编写一个 DBMS,以便深入理解该语言。由于性能是这里的重中之重,所以我尝试自己编写大部分代码,并且偶然发现了不安全 Rust 中的 UB 问题。

我想实现页面缓存功能,但由于所有权规则,无法从文件中获取页面,将其存储在缓存中,然后返回对其的引用。这是 BTree 管理器结构体的缓存方法:

核心.rs

fn load_page(&self, page_number: u32) -> Result<&mut Page, Box<dyn Error>> {
    let page = match self.load_from_cache(page_number) {
        None => self.load_from_file(page_number)?,
        Some(page) => return Ok(page),
    };
    Ok(unsafe { (*self.cache.get()).insert(page) })
}

fn load_from_cache(&self, page_number: u32) -> Option<&mut Page> {
    unsafe { (*self.cache.get()).find_mut(page_number) }
}

缓存.rs

pub fn find_mut(&mut self, page_number: u32) -> Option<&mut Page> {
    self.find_page(page_number)
        .and_then(|index| self.cache[index].as_mut())
}

fn find_page(&self, page_number: u32) -> Option<usize> {
    let mut begin = 0usize;
    let mut end = self.cache.len();
    let mut middle = (begin + end) / 2;
    while begin != end {
        match self.cache[middle] {
            None => begin = middle + 1,
            Some(ref page) => {
                if page.page_number() > page_number {
                    begin = middle + 1;
                } else if page.page_number() < page_number {
                    end = middle - 1;
                } else {
                    return Some(middle);
                }
            }
        }
        middle = (begin + end) / 2
    }
    None
}

pub fn insert(&mut self, page: Page) -> &mut Page {
    if self.size < self.cache.len() {
        self.upheap_and_get(page)
    } else {
        let to_discard = self.longest_unused.unwrap();
        self.cache[to_discard..].rotate_left(1);
        self.upheap_and_get(page)
    }
}

如果页面未缓存,则从文件加载:

核心.rs

fn load_from_file(&self, page_number: u32) -> Result<Page, Box<dyn Error>> {
    let mut buffer = match self.file.find_page(page_number) {
        None => return Err(todo!()),
        Some(buffer) => buffer,
    };
    let page = Page::deserialize(&mut buffer);
    Ok(page)
}

文件.rs

pub fn find_page(&self, page_number: u32) -> Option<&[u8]> {
    match self.load_page(page_number + 4096) {
        None => None,
        Some(slice) => Some(slice),
    }
}

pub fn find_page_mut(&mut self, page_number: u32) -> Option<&mut [u8]> {
    self.load_page(page_number + 4096)
}

fn load_page(&self, page_number: u32) -> Option<&mut [u8]> {
    let total_length = unsafe { (*self.buffer.get()).len() };
    let page_offset = PAGE_SIZE * page_number as usize;
    let page_end = page_offset + PAGE_SIZE;
    if page_end <= total_length {
        unsafe { Some(&mut (*self.buffer.get())[page_offset..page_end]) }
    } else {
        None
    }
}

这是项目中我选择退出所有权规则的唯一两个地方,其中

buffer
属于
UnsafeCell<MemmapMut>
类型。我已经采取了所有预防措施来确保
buffer
永远不会悬空,并且我或多或少相信所有使用返回值的方法都遵循所有权规则,因为返回的页面是存储在缓存中的拥有类型,这是归 BTree 所有。

这种方法会导致未定义的行为吗?

根据我在 The Rustonomicon 中发现的内容,我认为不应该,但由于我在这里使用不安全的代码,我无法完全确定我是否没有违反 BTree 之外的所有权规则,从而导致 UB。

rust undefined-behavior unsafe ownership
1个回答
0
投票

感谢@user2722968,我阅读了更多文档,发现最大的错误是使用从原始指针获取的引用。我决定尝试仅在最低级别使用原始指针,如果使用得当,这“不应该”不会导致 UB。再次感谢@user2722968,如果有人有任何建议请随时评论

© www.soinside.com 2019 - 2024. All rights reserved.