Это сообщение об ошибке действительно прискорбно, и я не думаю, что оно дает хорошее объяснение того, что здесь происходит.Проблема немного запутана.
Ошибка является локальной для функции advance_slice()
, так что это все, на что нам нужно обратить внимание.Тип элементов среза не имеет значения, поэтому давайте возьмем определение этой функции:
fn advance_slice_mut<T>(s: &mut &mut [T]) {
let new = &mut s[1..];
*s = new;
}
В первой строке создается новый объект среза, начинающийся после первого элемента исходного среза.
Почемуэто вообще разрешено?Разве у нас нет двух изменяемых ссылок на одни и те же данные сейчас?Исходный срез *s
включает новый срез new
, и оба позволяют изменять данные.Причина, по которой это допустимо, заключается в том, что *s
является неявным образом переназначенным при создании подлиса, и *s
не может использоваться снова в течение срока действия этого заимствования, поэтому у нас все еще есть только одна активная ссылка на данныев подл.Перезапись ограничена функцией advance_slice_mut()
, поэтому срок ее службы короче, чем у исходного среза, что является основной причиной получаемой ошибки - вы фактически пытаетесь назначить срез, который действует только до конца функции.в область памяти, которая живет дольше, чем вызов функции.
Этот вид неявного перезабора происходит каждый раз, когда вы вызываете функцию, которая принимает аргумент по изменяемой ссылке, в том числе при неявном вызове index_mut()
в &mut s[1..]
,Изменяемые ссылки не могут быть скопированы, так как это создаст две изменяемые ссылки на одну и ту же память, и разработчики языка Rust решили, что неявное повторное заимствование является более эргономичным решением, чем перемещение изменяемых ссылок по умолчанию.Однако повторное заимствование не происходит для общих ссылок, поскольку их можно свободно копировать.Это означает, что &s[1..]
будет иметь то же время жизни, что и исходная область, так как вполне нормально для двух перекрывающихся неизменных фрагментов сосуществовать.Это объясняет, почему определение вашей функции отлично работает для неизменяемых фрагментов.
Итак, как мы можем решить эту проблему?Я считаю, что то, что вы намереваетесь сделать, совершенно безопасно - после переназначения нового фрагмента старому, старый исчезает, поэтому у нас нет двух одновременных изменяемых ссылок на одну и ту же память.Чтобы создать срез с тем же временем жизни, что и исходный срез в безопасном коде, нам нужно переместить исходный срез из полученной ссылки.Мы можем сделать это, заменив его пустым фрагментом:
pub fn advance_slice_mut<T>(s: &mut &mut [T]) {
let slice = std::mem::replace(s, &mut []);
*s = &mut slice[1..];
}
В качестве альтернативы, мы также можем прибегнуть к небезопасному коду:
use std::slice::from_raw_parts_mut;
pub fn advance_slice_mut<T>(s: &mut &mut [T]) {
unsafe {
assert!(!s.is_empty());
*s = from_raw_parts_mut(s.as_mut_ptr().add(1), s.len() - 1);
}
}