Компилятор умнее, чем вы считаете. помешал вам ввести небезопасную память :
fn foo<'a, F>(func: &F)
where
F: Fn() -> Bar<'a>,
{
let number = 42;
let bar = (func)();
bar.number.set(Some(&number));
}
Этот код говорит, что вызывающий foo
может указать время жизни для 'a
, но затем тело метода сохраняет ссылку на значение. Эта сохраненная ссылка не гарантированно проживет так долго. В качестве очевидного примера вызывающая сторона может потребовать, чтобы 'a
== 'static
, но для функции было бы невозможно выполнить:
fn b() -> Bar<'static> {
Bar {
number: Cell::new(None),
}
}
fn main() {
foo(&b);
}
Обратите внимание, что это не имеет ничего общего с замыканиями или функциями:
use std::cell::Cell;
fn main() {
let number = Cell::new(None);
let x = 1;
number.set(Some(&x));
let y = 2;
number.set(Some(&y));
}
error[E0597]: `x` does not live long enough
--> src/main.rs:6:22
|
6 | number.set(Some(&x));
| ^ borrowed value does not live long enough
...
9 | }
| - `x` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
Почему первый пример работал, а не второй?
Поскольку компилятор знает, что Cell
(на самом деле UnsafeCell
) необходимо учитывать вероятность того, что вы будете хранить значение в созданном типе.
С Номикон , упор мой:
UnsafeCell<T>
, Cell<T>
, RefCell<T>
, Mutex<T>
и все другие типы внутренней изменчивости инвариантны относительно T
(как и метафора *mut T
)
Дисперсия - это плотная тема, которую я не могу объяснить кратко.
@ trentcl предоставляет этот пример, который показывает, что ваш исходный код может не работать так, как вы думаете.
Без Cell
компилятор знает, что безопасно автоматически настроить время жизни возвращаемого типа на немного более короткое. Если мы заставим тип быть длиннее 'a
, мы получим ту же ошибку:
fn foo<'a, F>(func: F)
where
F: Fn() -> Bar<'a>,
{
let number = 42;
let mut bar: Bar<'a> = func();
// ^^^^^^^
bar.number = Some(&number);
}
error[E0597]: `number` does not live long enough
--> src/main.rs:17:24
|
17 | bar.number = Some(&number);
| ^^^^^^ borrowed value does not live long enough
18 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 11:1...
--> src/main.rs:11:1
|
11 | / fn foo<'a, F>(func: F)
12 | | where
13 | | F: Fn() -> Bar<'a>,
14 | | {
... |
17 | | bar.number = Some(&number);
18 | | }
| |_^
Разве это невозможно без [...]
Да, но я не уверен точно, что это будет. Я полагаю, что для этого потребуется универсальных связанных типов (GAT) из RFC 1598 .
Моей первой мыслью было попробовать более высокие оценки черт (HRTB):
fn foo<F>(func: F)
where
F: for<'a> Fn() -> Bar<'a>,
{
let number = 42;
let bar = func();
bar.number.set(Some(&number));
}
Это вызывает E0582 :
error[E0582]: binding for associated type `Output` references lifetime `'a`, which does not appear in the trait input types
--> src/main.rs:17:25
|
17 | F: for <'a> Fn() -> Bar<'a>,
| ^^^^^^^
Если честно, я не вижу значения в коде на основе предоставленного примера. Если вы возвращаете Bar
по значению, вы можете сделать его изменяемым, устраняя необходимость во внутренней изменчивости.
Вы также можете изменить замыкание, приняв необходимое значение:
fn foo<F>(func: F)
where
F: for<'a> Fn(&'a i32) -> Bar<'a>,
{
let number = 42;
let bar = func(&number);
}
Смотри также: