可变借用,返回寿命更长的对象

6

我目前正在尝试将一个可变的切片/视图实现到支持安全地获取内存中消息遍历子切片的缓冲区中。一个最小的例子如下:

struct MutView<'s> {
    data: &'s mut [u8]
}

impl<'s> MutView<'s> {
    pub fn new(data: &'s mut [u8]) -> Self {
        MutView { data }
    }
    pub fn subview<'a>(&'a mut self, start: usize, end: usize) -> MutView<'a> {
        MutView::new(&mut self.data[start..end])
    }
    pub fn get<'a>(&'a mut self) -> &'a mut [u8] {
        self.data
    }
}

然而,这种设计存在一个问题,如果我在函数中本地创建了一个长度超过该函数作用域的 MutView<'s> (例如 's = 'static),那么我就无法从函数返回子视图。类似下面的代码就会有问题:
fn get_subview(data: &'s mut [u8]) -> MutView<'s> {
    MutView::new(data).subview(0, 5)
}

由于MutView::new(data)是本地临时变量,因此会产生编译错误,显然由subview()返回的MutView<'a>不能从该函数返回。
subview()的签名更改为:
pub fn subview<'a>(&'a mut self, start: usize, end: usize) -> MutView<'s> {
    MutView::new(&mut self.data[start..end])
}

由于borrow checker抱怨返回的对象的生命周期必须长于 &'a mut self,所以不可能。

我看到的问题是borrow checker被设置为处理subview()返回由&'a mut self拥有的数据的情况,而在这种情况下,我返回由某些底层缓存拥有并且比 lifetime 'a 更长的数据。因此出于安全原因,我仍然希望在返回子视图的lifetime期间进行&'a mut self的可变借用,同时允许&'a mut self在没有缩短子视图生命周期的情况下被丢弃。

我唯一看到的选项是添加一个消耗self的方法into_subviewinto_slice。但是对于我的特定情况,我正在生成具有给定模式的消息读取get / set方法,这意味着我有针对消息体中每个字段的get / set方法,并且添加额外的into_*方法会导致大量额外的代码生成/编译,以及额外的使用复杂性。因此,如果可能的话,我想避免这种情况。

目前在Rust中有没有好的处理这种依赖关系的方法?


抱歉,应该是 MutView,已经修复了。 - pfrommerd
我期望 subview() 的正确签名应该是 pub fn subview(&mut self, start: usize, end: usize) -> MutView<'s>(因为它返回一个新的 MutView,它引用旧的数据)- 但是那个无法编译。我怀疑问题与使用生命周期不变的 &mut 引用有关。修改后的代码 可以编译,使用共享引用。 - user4815162342
1个回答

3

subview 返回一个 MutView<'s> 是不安全的。

这将允许用户多次调用 subview 并产生潜在重叠的范围,违反了 Rust 引用保证的可变引用是独占的。对于不可变引用来说这很容易实现,因为它们可以共享,但对于可变引用则有更严格的要求。因此,从 self 派生出的可变引用必须绑定其生命周期到 self,以便在可变借用仍在使用时 "锁定" 对它的访问。编译器通过告诉你 &mut self.data[..]&'a mut [u8] 而不是 &'s mut [u8] 来强制执行这一点。

我唯一能想到的选择是添加一个消耗自身的 into_subviewinto_slice 方法。

这是我看到的主要选择,你需要保证的关键部分是互斥性,而消耗 self 将其从等式中去除。你也可以参考类似于 split_mut, split_at_mut, chunks_mut 等切片的可变方法,它们被精心设计以同时获得多个可变元素/子切片。

你可以使用 std::mem::transmute 强制生命周期符合你的要求(警告: transmute 非常不安全,容易使用不正确),但此后你将有责任自己维护上述引用保证。在这种情况下,subview() -> MutView<'s> 函数应标记为 unsafe,并要求范围是互斥的。我不建议除非你返回多个可变引用并已检查它们不重叠的特殊情况下使用。

我需要看到你希望设计的 API 才能给出更好的建议。



网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接