На stackowerflow мне объяснили как на самом деле работают времена жизни. Мне не слишком нравится эта концепция, но она объясняет что происходит в примере в шапке.
Суть вот в чем:
Длительность заимствования определяется только временем жизни в ссылке, которое вычисляется компилятором.
Компилятор в своих вычислениях конечно учитывает области видимости ссылок и обычно заимствование заканчивается, когда ссылка выходит из области видимости. Но в примере в шапке эти вычисления приводят к тому, что время жизни в ссылке должно быть равно области определения переменной data
и оно больше чем область видимости ссылки на data
.
Похоже, что единственная ситуация, когда явно проявляется то что заимствование может продолжаться после выхода ссылки из области видимости - это ссылка вида &'a mut T<'a>
. Во всяком случае я не смог найти других примеров. Если кто-нибудь сможет - кидайте сюда.
В рамках этой концепции становится понятно, почему правила заимствования не действуют на статические переменные. Мутабельное заимствование по ссылке с временем жизни 'static
должно длится вечно, и когда ссылка вышла бы из области видимости, доступ к данным был бы заблокирован навсегда.
Честно говоря не вижу смысла в такой реализации времен жизни. Назначение механизма заимствования - предотвращение гонок при изменении данных. Зачем может быть нужно продолжать блокировать доступ к данным после того, как мутабельная ссылка на эти данные, покинула область видимости? Всегда считал что время жизни в ссылке - это верхняя граница, до котрой эта ссылка может дожить, а длительность заимствования данных определяется тем, где ссылка покидает область видимости (или в случае NLL тем где ссылка последний раз использовалась).
Документация в этом вопросе не особенно вдается в подробности. Вот определение времени жизни из Растономикона:
Each reference, and anything that contains a reference, is tagged with a lifetime specifying the scope it’s valid for.
То есть время жизни определяет область в которой ссылка действительна. Не знаю можно ли “область в которой ссылка действительна” трактовать как “область в которой заимствование действительно”?
Хотя дальше есть пара примеров, в комментариях которых говорится что длительность заимствования равна выбранному времени жизни:
fn as_str<'a>(data: &'a u32) -> &'a str {
'b: {
let s = format!("{}", data);
return &'a s
}
}
fn main() {
'c: {
let x: u32 = 0;
'd: {
// An anonymous scope is introduced because the borrow does not
// need to last for the whole scope x is valid for. The return
// of as_str must find a str somewhere before this function
// call. Obviously not happening.
println!("{}", as_str::<'d>(&'d x));
}
}
}
'a: {
let mut data: Vec<i32> = vec![1, 2, 3];
'b: {
// 'b is as big as we need this borrow to be
// (just need to get to `println!`)
let x: &'b i32 = Index::index::<'b>(&'b data, 0);
'c: {
// Temporary scope because we don't need the
// &mut to last any longer.
Vec::push(&'c mut data, 4);
}
println!("{}", x);
}
}