Почему времена жизни отличаются, когда функция вызывается в замыкании и вызывается непосредственно в Rust?
В следующем примере кода:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
// this compiles and runs fine
let _v = optional_values.unwrap_or_else(|| default_values());
// this fails to compile
let _v = optional_values.unwrap_or_else(default_values);
}
последнее утверждение не компилируется с:
error[E0597]: 'values' does not live long enough
--> src/main.rs:8:49
|
8 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^ borrowed value does not live long enough
...
12 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
Я задаюсь вопросом:
- что происходит, что вызывает разницу в поведении между двумя последними утверждениями
- является ли первый
unwrap_or_else(|| default_values())
правильным способом обработки этого или есть лучший образец
Ответы
Ответ 1
Это происходит потому, что default_values
реализует Fn() → &'static [u32]
, но не for<'a> Fn() → &'a [u32]
. Черты являются инвариантными, поэтому вы не можете принудить "что-то, что реализует Fn() → &'static [u32]
", к "чему-то, что реализует Fn() → &'a [u32]
" (для некоторых 'a
меньше чем 'static
), хотя, логически говоря, default_values
может удовлетворить оба.
Когда он &'static [u32]
в замыкании, default_values()
возвращает &'static [u32]
, но он может быть немедленно приведен к &'a u32
, что делает само замыкание способным реализовать Fn() → &'a [u32]
(где &'a
определяется компилятором).
Что касается того, почему добавление as fn() → &'static [u32]
работает, я предполагаю, что компилятор может распознать, что указатель на функцию типа fn() → &'static [u32]
способен реализовать Fn() → &'a [u32]
для любого 'a
. Я не уверен, почему это не делает это для обычных функций и замыканий; возможно, будущая версия компилятора может быть достаточно умной, чтобы позволить исходный код.
Другое решение состоит в том, чтобы сделать тип default_values
тем, который может реализовать Fn
вам черту Fn
:
fn default_values<'a>() -> &'a [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
Вместо того, чтобы сказать "это функция, которая возвращает 'static
ссылку ", подпись здесь говорит:" Это функция, которая может возвращать ссылку любого времени жизни ". Мы знаем, что "ссылка на любое время жизни" должна быть 'static
ссылкой", но компилятор видит сигнатуры как разные, потому что эта имеет дополнительную степень свободы. Этого изменения достаточно, чтобы ваш исходный пример компилировался.
Ответ 2
Нет разницы между замыканием и прямым вызовом функции: это просто вопрос вывода типа.
замыкание, которое компилирует:
let _v = optional_values.unwrap_or_else(|| default_values());
let _v = optional_values.unwrap_or_else(|| -> & [u32] {default_values()});
замыкание, которое не компилируется:
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values()});
функция, которая компилирует:
let _v = unwrap_or_else(optional_values, default_values as fn() -> &'static _);
функция, которая не компилируется:
let _v = unwrap_or_else(optional_values, default_values);
Небольшое объяснение
Рассмотрим этот эквивалентный код:
fn default_values() -> &'static [u32] {
static VALUES: [u32; 3] = [1, 2, 3];
&VALUES
}
fn unwrap_or_else<T, F>(slf: Option<T>, f: F) -> T where
F: FnOnce() -> T, {
match slf {
Some(t) => t,
None => f()
}
}
следующий фрагмент:
fn main() {
let values: [u32; 3] = [4, 5, 6];
let optional_values: Option<&[u32]> = Some(&values);
let _v = unwrap_or_else(optional_values, || -> &'static [u32] {default_values});
// the above throws the same error of:
//let _v = unwrap_or_else(optional_values, default_values);
}
терпит неудачу:
error[E0597]: 'values' does not live long enough
--> src/main.rs:18:48
|
18 | let optional_values: Option<&[u32]> = Some(&values);
| ^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that 'values' is borrowed for ''static'
...
27 | }
| - 'values' dropped here while still borrowed
Посмотрите со стороны мономорфизации: предположим, что компилятор делает вывод, что T
разрешает конкретный тип &'static [u32]
, и предположим, что созданный код выглядит примерно так:
fn unwrap_or_else_u32_sl_fn_u32_sl(slf: Option<&'static [u32]>,
f: fn() -> &'static [u32]) -> &'static [u32] {
...
}
тогда приведенная выше мономорфизация объясняет ошибку:
slf
значение optional_values
: Option<&'a [u32]>
, что не дожить и явно не может быть брошена, поскольку она не удовлетворяет 'static
требования пожизненного.
Если вы напишите:
let _v = unwrap_or_else(optional_values, || default_values());
// the same, expliciting the return type:
let _v = unwrap_or_else(optional_values, || -> & [u32] {default_values()});
Она составляет: в настоящее время жизни возвращаемого типа совместим с optional_values
жизни.
Наконец, я не могу объяснить почему, но факты говорят о том, что приведение as fn() → &'static _
помогает компилятор, чтобы быть уверенным, что развязку сроки службы, связанные с optional_values
и default_values
безопасен.