Ошибка времени жизни в структуре, ссылающейся на параметризованную функцию - PullRequest
0 голосов
/ 20 ноября 2018

Если я напишу код ниже, я получу error[E0309]: the parameter type 'T' may not live long enough.

struct Function<T> {
    f: fn() -> T,
}

struct FunctionRef<'f, T> {
    f: &'f Function<T>,
}

Это исправляет ошибку:

struct FunctionRef<'f, T: 'f> {
    f: &'f Function<T>,
}

Однако, насколько я могу судить, T не связан с временем жизни 'f. Действительно, T - это новый объект, созданный при запуске функции типа fn () -> T.

Где я что-то упустил?

1 Ответ

0 голосов
/ 20 ноября 2018

Краткий ответ: вам нужно T: 'f, потому что T может содержать поле, содержащее ссылки, а fn() -> T является ковариантным по отношению к T.

Чтобы упростить вещи, может помочь понять ...

На мгновение замените fn() -> T на T, потому что для меня проще объяснить, что происходит с жизнями. См. Примечание ниже, почему при такой замене не изменяется ошибка, связанная с временем жизни.

struct Function<T> {
    f: T,
}

struct FunctionRef<'f, T> {
    f: &'f Function<T>,
}

Это приводит к той же самой ошибке error[E0309]: the parameter type 'T' may not live long enough.

FunctionRef экземпляры не могут пережить ссылку, которая содержится в поле f: вы объявляете общий параметр времени жизни 'f в угловых скобках, а затем используете 'f в качестве аннотации внутри структуры тела. См. Также книгу .

Но FunctionRef::f зависит от параметра типа T. Явное ограничение T: 'f гарантирует, что T экземпляров не переживает ссылки, удерживаемые T, а FunctionRef не переживает FunctionRef::f.

Если это может помочь понять, замените универсальный T конкретным Foo типом:

struct Foo<'a> {
    n: &'a i32,
}

struct FunctionRef<'f, 'a: 'f> {
    f: &'f Foo<'a>,
}

Необходимо ограничить время жизни 'a, чтобы оно действовало по крайней мере столько, сколько 'f время жизни, в противном случае правила безопасности памяти были бы нарушены.

Примечание

Я рассмотрел случай f: T эквивалент f: fn() -> T, потому что такой конструктор типов ковариантен над T.

Чтобы понять, что значение fn() -> T является ковариантным по отношению к T, рассмотрим следующую структуру:

struct Foo<'a> {
    v: &'a i32
}

В этом случае безопасно присвоить v значение со временем жизни "больше", чем 'a, например:

let ref_value: &'static i32 =  &100;
let foo = Foo { v: ref_value};

Теперь то же самое верно для следующей структуры:

struct Function<'a> {
    f: fn() -> &'a i32
}

Поле f ожидает функцию, которая возвращает &i32, который переживет 'a.

В этом случае безопасно передать функцию, которая возвращает &i32 с «большим» временем жизни, например:

fn my_f() -> &'static i32 {
    &100
}

fn main() {
    let foo = Function { f: my_f};
}

За этим стоит довольно много теорий типов, подробное объяснение см. В терминологии .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...