Известно, что эта проблема решается разделением времен жизни в функции:
fn test<'a,'b>(x:&'a mut &'b u8){ }
Вопрос в том, почему вообще это происходит?
Одинаковые времена жизни должны приводить только к тому, что ссылка data будет не 'static, а ограничится локальной областью функции main().
Нашел древний вопрос на SO в котором обсуждается похожий случай. Там пришли к выводу, что происходит самозаимствование.
В моем примере никакого самозаимствования теоретически не может быть.
Первая ссылка должна дожить до println + вторая (изменяемая ссылка) должна иметь точно такое же время жизни -> println не может использовать первую ссылку, потому что жива вторая (изменяемая).
Если убрать println, то все собирается, потому что не возникает конфликта уникальности.
Время жизни самой ссылки не обязательно должно быть равно времени жизни данных на которые она указывает. Оно вполне может быть меньше, и для мутабельных ссылок тоже. Вторая ссылка не обязана быть живой весь интервал, вж первой - это верхний предел второй.
Даже если переписать вот так, то ничего не меняется:
let mut data = &1;
{
let t = &mut data;
test(t);
}
println!("{:?}", data);
Тут вторая ссылка t никак не может быть живой к моменту вызова println!(). А в сообщении об ошибке Rust пишет, что mutable borrow длится до конца main().
Это все да, но ты сам в сигнатуре явно требуешь что бы время жизни было одинаковым - оно и становится одинаковым, т.е. весь main. БЧ же довольно прямолинейная штуковина.
По-моему ты путаешь вж самой ссылки и вж данных на котрые она указывает. Тут одно правило для всех - вж ссылки не может быть больше чем вж данных на которые она указывает. Вот это 'a - это как раз время жизни значения 1u8.
Тут время жизни первой ссылки (самой переменной data) должно быть равно времени жизни значения на которое она указывает из-за того, что вторая ссылка - мутабельная (инвариантность и все дела). А время жизни второй ссылки (переменной t) определяется только скопом в котором она объявлена.
Мне кажется что из-за явного требования одинаковости вж это просто некорректный код, который в принципе не должен работать. Но возможно я и правда туплю, хз. @IBUzPE9, ты не против если я продублирую вопрос с { let t = &mut data; test(t);} на англоязычный SO? Вдруг кто из команды ядра посмотрит и прям по полочкам все разложит.
Что ж, на мою попыткуShepmaster отправил искать отличия от этого вопроса:
Который разжевывает то же, что я выше пытался сказать - жесткое требование одного времени жизни невозможно удовлетворить.
Правда, меня все равно немного смущает что в сообщении об ошибке говорится не об этом, а об уникальности. Но, видимо, код просто сломан слишком сильно.
Программа работает во время выполнения функции main. Время жизни static действует в это же время (условно, есть же еще и константы и статические переменные). У data вж будет static, оно не укоротится из-за того, что используется в test. Ответ на похожий вопрос: Why does RefCell influence lifetimes? - #2 by matklad - help - The Rust Programming Language Forum, — иначе никак не объяснить, мне кажется
@ozkriff Ну я бы не сказал что он там прямо все разжевал. По сути просто сказал что так нельзя и надо ввести разные времена жизни. Вобще там рассматривается более запутанный случай и понять, что происходит что-то не то гораздо сложнее.
@Virtuos86 Может быть я не совсем понятно сформулировал. Суть в том что если явно задать вж 'static:
let mut data:&'static u8 = &1;
{
let t = &mut data;
test(t);
}
println!("{:?}", data);
Rust начинает ругаться что data недостаточно долго живет и что она должна быть 'static. А если вызов test() закомментировать все будет работать. Это как раз проявляется эффект инвариантности мутабельных ссылок о котором выше сказал @matklad.
Но все эти интересные эффекты заимствование должно закончиться когда t покидает область видимости. А оно почему-то не заканчиваются.
@matklad Нету такой доки. Раньше это совсем вскользь упоминалось в номиконе, сейчас остался только русский перевод. Там сложно что-то понять.
Я про это когда-то загадку придумал, наверно все уже видели.
Так а почему должны? Вроде бы не должны, ровно потому что нет reborrowing. Сигнатура test говорит, что вж в t и в data ровно совпадают, и вж в data должно как минимум покрывать println, значит и вж в t должно его покрывать. Ну и дальше, в зависимости от скоупа, получаем либо вж, которое оказывается дольше дольше скоупа переменной, либо уникальную и шаренную ссылку одновременно.
Я думаю, вся фигня в том, что текущая система лайфтаймов лексическая. Конечно, t уничтожится, но системе на это пофиг. Если в сигнатуре test указано, что обе ссылки имеют одинаковое время жизни, но они и не используются в функции (а значит, и не удаляются по выходе из нее, т.е. “внутренний”, более узкий лайфтайм не используется), значит берется другой лайфтайм, и обе ссылки считаются валидными до конца main.
Запостил альтернативный ответ с отсылкой к твоему вопросу. По-моему, не стоило соглашаться что это дубликат.
Вот облом, такое решение небезопасно. Придется удалить, вот что там было.
То есть получается что так делать тоже нельзя?
let mut data:&'static u8 = &1;
{
let mut s = &*data;
let t = &mut s;
test(t);
}
println!("{:?}", data);
Кажется я нашел почему заимствование продолжается. Если бы оно заканчивалось то вот это бы приводило к использованию после освобождения:
fn test<'a>(_x:&'a mut &'a str){}
fn main() {
let mut data:&'static str = "hello";
{
let x = "ups".to_owned();
let mut s = &*data;
let t = &mut s;
test(t);
*t = &x;
}
println!("{:?}", data);
}
Да нет тут все было бы норм. После перезаимствования через t можно менять s, а data уже никак с t не связана и ее изменить нельзя.
А в своем ответе я этим “волшебным” трансмьютом как раз переписал времена жизни как-будто t ссылается на локальную переменную s, в то время как она указывала на внешнюю data.
Заслуженный фейл.