Есть несколько вещей, которые нам нужно знать, чтобы понять это. Во-первых, это тип строкового литерала. Любой строковый литерал (например, "foo"
) имеет тип &'static str
. Это ссылка на фрагмент строки, но, кроме того, это ссылка stati c. Этот вид ссылки длится на всю длину программы и может быть принудительно приведен к любому другому времени жизни.
Это означает, что в вашем первом фрагменте кода x и y уже являются и ссылками, и имеют тип &'static str
. Причина, по которой вызов longest(&x, &y)
все еще работает (даже если &x
и &y
имеют тип &&'static str
), вызвана принудительным разыменованием. longest(&x, &y)
действительно обесцвечивается как longest(&*x, &*y)
для приведите типы в соответствие.
Давайте проанализируем времена жизни в первом фрагменте кода.
fn main() {
// x: &'static str
let x = "eee";
// Using let patterns in a forward declaration doesn't really make sense
// It's used for things like
// let (x, y) = fn_that_returns_tuple();
// or
// let &x = fn_that_returns_reference();
// Here, it's the same as just `let m;`.
let &m;
{
// y: &'static str
let y = "tttt";
// This is the same as `longest(x, y)` due to autoderef
// m: &'static str
m = longest(&x, &y);
}
// `m: &static str`, so it's still valid here
println!("ahahah: {}", m);
}
(детская площадка)
С let &m;
возможно, вы имели в виду что-то вроде let m: &str
, чтобы вызвать его тип. Я думаю, что на самом деле гарантирует, что время жизни ссылки в m
начинается с этой предварительной декларации. Но поскольку m
имеет тип &'static str
, это не имеет значения.
Теперь давайте рассмотрим вторую версию с i64
.
fn main() {
// x: i64
// This is a local variable
// and will be dropped at the end of `main`.
let x = 3;
// Again, this doesn't really make sense.
let &m;
// If we change it to `let m: &i64`, the error changes,
// which I'll discuss below.
{
// Call the lifetime roughly corresponding to this block `'block`.
// y: i64
// This is a local variable,
// and will be dropped at the end of the block.
let y = 5;
// Since `y` is local, the lifetime of the reference here
// can't be longer than this block.
// &y: &'block i64
// m: &'block i64
m = ooo(&x, &y);
} // Now the lifetime `'block` is over.
// So `m` has a lifetime that's over
// so we get an error here.
println!("ahahah: {}", m);
}
(детская площадка)
Если мы изменим объявление m
на let m: &i64
(что я и имел в виду), ошибка изменится.
error[E0597]: `y` does not live long enough
--> src/main.rs:26:21
|
26 | m = ooo(&x, &y);
| ^^ borrowed value does not live long enough
27 | } // Now the lifetime `'block` is over.
| - `y` dropped here while still borrowed
...
30 | println!("ahahah: {}", m);
| - borrow later used here
(детская площадка)
Так что теперь мы явно хотим, чтобы m
длился столько же, сколько и внешний блок, но мы не можем сделать так, чтобы y
длился так долго, поэтому ошибка происходит в вызов ooo
.
Поскольку обе эти программы работают с литералами, мы фактически можем сделать компиляцию второй версии. Для этого мы должны воспользоваться предложением stati c. Хорошее резюме того, что это значит, можно найти в объявлении Rust 1.21 (в котором был выпущен этот выпуск) или в этом вопросе .
Короче говоря, если мы непосредственно возьмем ссылку на буквальное значение, эта ссылка может быть повышена до статической c ссылки. То есть он больше не ссылается на локальную переменную.
fn ooo<'a>(x: &'a i64, y: &'a i64) -> &'a i64 {
if x > y {
x
} else {
y
}
}
fn main() {
// due to promotion
// x: &'static i64
let x = &3;
let m;
{
// due to promotion
// y: &'static i64
let y = &5;
// m: &'static i64
m = ooo(x, y);
}
// So `m`'s lifetime is still active
println!("ahahah: {}", m);
}
(детская площадка)