Могу ли я переназначить ссылку на изменяемый фрагмент на свой фрагмент? - PullRequest
4 голосов
/ 15 апреля 2020

Я реализую стекоподобную структуру, где структура содержит изменяемую ссылку на фрагмент.

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>`

Даже упрощенная версия 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];
    }
}

, что дает

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];
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^

Есть ли способ заставить это работать, или мне нужно более четко отслеживать границы интересующего меня фрагмента?

Ответы [ 2 ]

4 голосов
/ 15 апреля 2020

Я немного изменил код Масклинна, чтобы в одном и том же стеке можно было вызывать несколько .pop() s:

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 и временно заменить его пустым срезом, , который может быть любым временем жизни . Теперь мы можем сделать что-нибудь с data, не связывая его с временем жизни &mut self.

1 голос
/ 15 апреля 2020

Для подвопроса 2 необходимо указать соотношение между &mut self и 'a, в противном случае они считаются не связанными. Я не знаю, есть ли ярлык через жизненное ограничение, но если вы укажете, что self живет для 'a, у вас все хорошо.

Для подвопроса 1 компилятор "не видит сквозь" msgstr "вызовы функций (в том числе индексирование, которое десугарсирует вызов функции), поэтому он не знает, что &self.data[n - 1] и &mut self.data[0..n-1] не перекрываются. Вам необходимо использовать split_mut_last.

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

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

детская площадка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...