【问题标题】:Mutable borrow with returned object of longer lifetime具有更长生命周期的返回对象的可变借用
【发布时间】:2021-12-31 14:57:32
【问题描述】:

我目前正在尝试在缓冲区中实现一个可变切片/视图,该缓冲区支持安全地获取子切片以进行内存中消息遍历。一个最小的例子是

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&lt;'s&gt; 并且 's 比函数的范围长(比如 's = 'static),我有无法从函数返回子视图。类似的东西

fn get_subview(data: &'s mut [u8]) -> MutView<'s> {
    MutView::new(data).subview(0, 5)
}

由于MutView::new(data) 是一个本地临时变量,因此出现编译错误,因此很明显subview() 返回的MutView&lt;'a&gt; 无法从函数中返回。

subview()的签名改为

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

不可能,因为借用检查器抱怨返回对象的生命周期必须长于 &amp;'a mut self 的生命周期。

我看到的问题是借用检查器被设置为处理 subview() 返回由 &amp;'a mut self 拥有的数据的情况,而在这种情况下,我返回的数据属于某个底层缓冲区拥有的寿命超过生命周期的数据'a ,因此出于安全原因,我仍然希望在返回对象的生命周期内对&amp;'a mut self 进行可变借用,同时还允许在不缩短返回子视图生命周期的情况下删除&amp;'a mut self

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

目前在 Rust 中是否有处理这种依赖的好方法?

【问题讨论】:

  • 抱歉,应该是 MutView 并且已经修复。
  • 我希望subview() 的正确签名是pub fn subview(&amp;mut self, start: usize, end: usize) -&gt; MutView&lt;'s&gt;(因为它返回一个新的MutView,它引用旧的数据) - 但doesn't compile 要么.我怀疑这个问题与使用生命周期不变的&amp;mut 引用有关。修改后的代码compiles 共享引用。

标签: rust borrow-checker


【解决方案1】:

subview 返回一个MutView&lt;'s&gt; 是不合理的。

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

我看到的唯一选择是添加一个消耗 self 的 into_subviewinto_slice 方法。

我看到的主要选项,您需要保证的关键部分是排他性,使用 self 会将其从等式中删除。您还可以从 split_mutsplit_at_mutchunks_mut 等切片上的可变方法中获得灵感,这些方法经过精心设计,可以同时获取多个可变元素/子切片。

可以使用 std::mem::transmute 强制生命周期为您想要的(警告:transmute 非常适合 unsafe 并且很容易误用) ,但是,您有责任自己维护上述参考保证。然后应将subview() -&gt; MutView&lt;'s&gt; 函数标记为unsafe,并带有范围是排他的安全要求。我不建议这样做,除非在您返回多个可变引用并检查它们不重叠的特殊情况下。

我必须确切了解您希望设计什么样的 API 才能提供更好的建议。

【讨论】:

  • 这个问题主要与 capnproto crate 的设计有关,特别是github.com/capnproto/capnproto-rust/issues/241。我主要想确保我没有错过处理这种情况的既定方法。对于常规切片,最终使用时 self 的消耗和早期使用的隐式 &mut 借用是通过重借来处理的,经过进一步研究,似乎添加类似引用的重借(以及其他不错的功能,如隐式 &mut --> &)转换似乎仍然是一个悬而未决的问题(请参阅github.com/rust-lang/rfcs/issues/1403)。
猜你喜欢
  • 2017-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-18
相关资源
最近更新 更多