我是否可以将可变切片引用重新分配给自身的子切片?

问题描述 投票:4回答:2

我正在实现类似堆栈的结构,其中该结构包含对切片的可变引用。

struct StackLike<'a, X> {
    data: &'a mut [X],
}

我希望能够从堆栈中弹出最后一个元素,例如:

impl<'a, X> StackLike<'a, X> {
    pub fn pop(&mut self) -> Option<&'a X> {
        if self.data.is_empty() {
            return None;
        }
        let n = self.data.len();
        let result = &self.data[n - 1];
        self.data = &mut self.data[0..n - 1];
        Some(result)
    }
}

此失败:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/lib.rs:11:23
   |
11 |         let result = &self.data[n - 1];
   |                       ^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 6:5...
  --> src/lib.rs:6:5
   |
6  | /     pub fn pop(&mut self) -> Option<&'a X> {
7  | |         if self.data.is_empty() {
8  | |             return None;
9  | |         }
...  |
13 | |         Some(result)
14 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:11:23
   |
11 |         let result = &self.data[n - 1];
   |                       ^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 5:6...
  --> src/lib.rs:5:6
   |
5  | impl<'a, X> StackLike<'a, X> {
   |      ^^
note: ...so that the expression is assignable
  --> src/lib.rs:13:9
   |
13 |         Some(result)
   |         ^^^^^^^^^^^^
   = note: expected  `std::option::Option<&'a X>`
              found  `std::option::Option<&X>`

即使a simplified version of pop不返回值,仅缩小切片也不起作用。

pop

给出

impl<'a, X> StackLike<'a, X> {
    pub fn pop_no_return(&mut self) {
        if self.data.is_empty() {
            return;
        }
        let n = self.data.len();
        self.data = &mut self.data[0..n - 1];
    }
}

是否有办法使这项工作有效,或者我需要更明确地跟踪我感兴趣的切片的边界?

rust lifetime borrowing
2个回答
2
投票

我略微修改了Masklinn的代码,以允许在同一堆栈上调用多个error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements --> src/lib.rs:11:26 | 11 | self.data = &mut self.data[0..n - 1]; | ^^^^^^^^^^^^^^^^^^^ | note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 6:5... --> src/lib.rs:6:5 | 6 | / pub fn pop_no_return(&mut self) { 7 | | if self.data.is_empty() { 8 | | return; 9 | | } 10 | | let n = self.data.len(); 11 | | self.data = &mut self.data[0..n - 1]; 12 | | } | |_____^ note: ...so that reference does not outlive borrowed content --> src/lib.rs:11:26 | 11 | self.data = &mut self.data[0..n - 1]; | ^^^^^^^^^ note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 5:6... --> src/lib.rs:5:6 | 5 | impl<'a, X> StackLike<'a, X> { | ^^ note: ...so that reference does not outlive borrowed content --> src/lib.rs:11:21 | 11 | self.data = &mut self.data[0..n - 1]; | ^^^^^^^^^^^^^^^^^^^^^^^^

.pop()

这里最棘手的部分是这一行(为明确起见,我添加了类型注释):

struct StackLike<'a, X> {
    data: &'a mut [X],
}

impl<'a, X> StackLike<'a, X> {
    pub fn pop(&mut self) -> Option<&'a mut X> {
        let data = std::mem::replace(&mut self.data, &mut []);
        if let Some((last, subslice)) = data.split_last_mut() {
            self.data = subslice;
            Some(last)
        } else {
            None
        }
    }
}

fn main() {
    let mut data = [1, 2, 3, 4, 5];
    let mut stack = StackLike { data: &mut data };

    let x = stack.pop().unwrap();
    let y = stack.pop().unwrap();
    println!("X: {}, Y: {}", x, y);
}

我们暂时将let data: &'a mut [X] = std::mem::replace(&mut self.data, &mut []); 替换为空切片,以便我们可以分割切片。如果您只写]

self.data

编译器会很不高兴:

let data: &'a mut [X] = self.data;

据我了解,问题在于error[E0312]: lifetime of reference outlives lifetime of borrowed content... --> src/main.rs:7:33 | 7 | let data: &'a mut [X] = self.data; | ^^^^^^^^^ | note: ...the reference is valid for the lifetime `'a` as defined on the impl at 5:6... --> src/main.rs:5:6 | 5 | impl<'a, X> StackLike<'a, X> { | ^^ note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 6:5 --> src/main.rs:6:5 | 6 | / pub fn pop(&mut self) -> Option<&'a mut X> { 7 | | let data: &'a mut [X] = self.data; 8 | | if let Some((last, subslice)) = data.split_last_mut() { 9 | | self.data = subslice; ... | 13 | | } 14 | | } | |_____^ 是可变引用,可变引用不是self.data(请记住,一次只能有一个)。并且您不能移出Copy,因为self.data是可变引用,而不是所有者。因此,编译器尝试做的是重新借入self,并在self.data的生存期内对其进行“感染”。这是一个死胡同:我们希望引用对于&mut self有效,但实际上仅在'a的生命周期内有效,并且这些生命周期通常是不相关的(并且不需要关联),这使编译器感到困惑。

[为了帮助编译器,我们使用&mut self明确地将切片从std::mem::replace中移出,并暂时将其替换为空切片self.data。现在,我们可以使用which can be any lifetime进行任何操作,而不会与data的生存期纠缠在一起。


1
投票

对于子问题2,您需要指出&mut self&mut self之间的关系,否则将它们视为无关。我不知道是否有通过生存期省略的快捷方式,但是如果您指定'a代表self,那么您就可以了。

对于子问题1,编译器不会“透视”函数调用(包括索引到函数调用的索引),因此它不知道'a&self.data[n - 1]是不重叠的。您需要使用&mut self.data[0..n-1]

split_mut_last

split_mut_last

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