Рассмотрим следующий простой пример:
use std::cell::RefCell;
// Compiles fine
fn good(t: RefCell<String>) -> bool {
t.borrow().len() == 12
}
// error[E0597]: `t` does not live long enough
fn bad(t: RefCell<String>) -> bool {
let t = t;
t.borrow().len() == 12
}
Функция bad не может скомпилироваться со следующей ошибкой:
error[E0597]: `t` does not live long enough
--> src/lib.rs:9:5
|
8 | let t = t;
| - binding `t` declared here
9 | t.borrow().len() == 12
| ^---------
| |
| borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
10 | }
| -
| |
| `t` dropped here while still borrowed
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, String>`
|
= note: the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
9 | let x = t.borrow().len() == 12; x
| +++++++ +++
Мне это кажется крайне странным. Простое переназначение параметра функции локальным переменным приводит к сбою компиляции. Не могли бы вы объяснить, почему?

Об этом говорится в Справке:
Временные объекты, созданные в конечном выражении тела функции, удаляются после любых именованных переменных, связанных в теле функции. Их областью удаления является вся функция, поскольку не существует меньшей охватывающей временной области.
Я не уверен, необходимо ли это для функционирования языка, делает язык более удобным или просто так было изначально реализовано компилятор, но менять сейчас было бы обратно несовместимо. Если кто-то знает больше, прокомментируйте или отредактируйте этот ответ.
Я знал об этом, и иногда это возникает при понимании порядка выпадения. Новым для меня является то, что аргументы функции, судя по всему, отбрасываются еще позже. Об этом почти упоминается здесь, но не совсем. Таким образом, порядок выпадения должен быть:
Вот программа, которая это демонстрирует.
struct A(u32);
impl A {
fn nothing(&self) {}
}
impl Drop for A {
fn drop(&mut self) {
println!("drop {}", self.0);
}
}
fn f(_a1: A) {
let _a3 = A(3);
A(2).nothing()
}
fn main() {
let a = A(1);
f(a);
}
Он печатает следующее:
drop 3
drop 2
drop 1
В вашем коде последнее выражение создает временный std::cell::Ref<'_, String>. Поскольку Ref имеет поведение Drop — в частности, время жизни содержится в типе, реализующем Drop — в отличие от обычной общей ссылки & требуется, чтобы время жизни было действительным в момент удаления. Поскольку он заимствован из RefCell<String>, Ref необходимо отбросить раньше, чем RefCell. И согласно приведенным выше правилам, это не выполняется, когда RefCell привязано внутри функции.
Обходного пути в сообщении об ошибке всегда должно быть достаточно.
Похоже, чтобы избежать такого неочевидного поведения и запутать потенциального читателя кода, лучше избегать создания временных переменных в операторах возврата.
@SomeName Не обязательно. Если временное не одолжено у местного жителя, то это не имеет значения. Например, можно вернуть String::new().len().
@SomeName Я думаю, что в целом разумно быть осторожным с типами, которые реализуют Drop (помимо освобождения). Это вызывает только комбинация Drop, заимствования и неясного порядка выпадения, и Drop является самым редким из этих трех.
@drewtato: «Я не уверен, что это необходимо…» — ваша цитата из «Справочника» объясняет, что «не существует меньшей охватывающей временной области». Какую меньшую охватывающую область, по вашему мнению, мог бы иметь временный объект вместо этого?
@eggyal Если обходной путь всегда работает, то ржавчина может применить его неявно, и я не могу придумать ни одной ситуации, в которой он не работал бы.
Честно говоря, я даже не понимаю, почему
badне компилируется, поскольку возвращаемое значение вообще не заимствует.