Как работают пожизненные ограничения на структуры в Rust?
Вчера в IRC обсуждался этот вопрос, и я чувствовал смутное недовольство.
Вопрос был:
Как вы определяете время жизни структуры, чтобы ограничить ее содержимое только теми вещами, которые живут так же долго, как и "себя".
то есть "сам" такого рода вещи.
Моя первая реакция была: ты не можешь.
Если вы создаете структуру Foo<'a>, время жизни, связанное с ней, определяется из ссылок, которые она содержит; если структура не содержит ссылку на себя (это невозможно), у вас не может быть такого "собственного времени жизни".
Об этом было много болтовни, и в итоге я написал этот манеж:
#[deriving(Show)]
struct Bar;
#[deriving(Show)]
struct Foo<'a> {
a:&'a Bar,
b:&'a Bar
}
fn factory<'a>(v1:&'a Bar, v2: &'a Bar) -> Foo<'a> {
return Foo {
a: v1,
b: v2
};
}
fn main() { // <---- Let's call this block lifetime 'one
let a = Bar;
let c = &a; // <-- C has lifetime 'one
{ // <------------ Let's call this block lifetime 'two
let b = Bar;
let mut foo1 = factory(c, c);
foo1.b = &b;
let mut foo2 = factory(&b, &b);
foo2.a = &a;
println!("{}", foo1);
println!("{}", foo2);
}
}
Тем не менее, я сейчас больше сбит с толку, чем меньше.
Итак, в строгом смысле в вышесказанном:
- с "один"
- и б имеет два
- 'static>' one> 'two (то есть' two ограничено 'one).
- у foo1 есть один
- у foo2 есть два
Теперь моя путаница:
Foo<'a> указывает, что' a - это минимальная граница времени жизни, которая может содержаться в экземпляре Foo.
Поскольку 'one>' two, foo2 должен содержать a &'one a; это работает.
Поскольку 'two>' one, foo1 не должен содержать &'two b; Однако это работает.
Зачем?
Похоже, моя путаница возникает из-за одного из двух заблуждений; или:
1) Экземпляр foo1 находится на лице Foo<'two>, а не Foo<' one>.
Я не понимаю, почему это так, поскольку он изготовлен на фабрике<'a>, где <' a> - время жизни c; что "один, а не" два. Нет абсолютно никакого способа, которым c может быть &'два в примере выше. Время жизни 'два недоступно на фабрике функций, где создается Foo.
2) Структурные жизни не работают так, как я понимаю, как они работают; то есть. время жизни 'a на экземпляре Foo может как-то меняться после создания экземпляра (например, на ходу?)
... но я не знаю какой.
1 ответ
Параметры времени жизни на ссылках противоречивы: при необходимости их можно заменить более коротким сроком службы.
По сути, вы ошиблись. Когда у вас есть &'one Bar
вы не можете назначить ссылку на значение с более коротким временем жизни (например, 'two
здесь), в противном случае ссылка будет зависать, когда выполнение покидает 'two
объем. Тем не менее, когда у вас есть &'two Bar
можно назначить ссылку на значение с более длительным сроком службы (например, 'one
а также 'static
), потому что ссылка выйдет из области видимости раньше, чем референт.
Почему ваша программа компилируется? Компилятор не только использует информацию из обращений к factory
выбрать подходящий срок жизни; он также использует информацию из заданий. &a
имеет тип &'one Bar
а также &b
имеет тип &'two Bar
, Так как 'two
начинается после 'one
и заканчивается раньше 'one
компилятор может вызвать &'one Bar
к &'two Bar
, В объектно-ориентированных терминах &'one Bar
это &'two Bar
(&'one Bar
это подтип &'two Bar
). Так же, как в Java, вы можете передать String
в качестве аргумента функции, которая ожидает Object
, Просто отношение подтипов - это обратный путь на всю жизнь.
Это означает, что мы нашли общий тип для &a
а также &b
: &'two Bar
, Таким образом, компилятор выводит 'two
за 'a
в звонках factory
,
Обратите внимание, что тип foo2
не изменяется при назначении; тип значения всегда статический.