Rust Lang Beginners
15.07.2026 15:28
Если все понятно в теории, но не понятно именно практика и применимость к расту, то можно так сократить описание:
В расте вся вариантность только в лайфтаймах
и по факту все сводится к тому можно ли привести один тип с лайфтаймом к другому типу с лайфтаймом.
1. covariant
struct Foo<'lf> {
field: &'lf str
}
В такой структуре можно сужать лайфтайм параметра (чтоб например два разных типа с одним лайфтаймом принимать в функции)
fn compare<'lf> (one: Foo<'lf>, another: &'lf str) -> bool {..}
Самый полезный вариант отношений.
2. invariant
struct Bar<'lf> {
field: Cell<&'lf u32>
}
В такой структуре нельзя менять (расширять или сужать) лайфтайм, он является "маркером"
см. брендирование в GhostCell
Логика простая: У тебя в структуре некоторые данные которые могут меняться, значит они должны сохранять лайфтайм оригинала, чтоб случайно не получилось "сузить" и заменить поле значением меньше.
По этому все инвариантные лайфтаймы будут где-то где есть мутабельность T в Cell<T>/ UnsafeCell<T>/ &mut T
3. contrvariant
в основном выражается через функции (там она и применима)
struct Baz<'any>{
field: fn(&'any u32)
}
тут действует обратная логика, т.е. лайфтайм можно "расширить" (по факту ты выражаешь не лайфтайм, а дырку, по этому это описание не совсем точное), например до статика:
let baz_static: Baz<'static> = baz;
Логика такая: Если можно принимать любой лайфтайм, то можем принимать и 'static
ps: в большинстве случаев при создании своих структур тебе ничего не надо делать, т.к. раст выведет отношение сам, но когда делаешь какойто врапер, коллекцию и не хранишь реальный T иногда важно проставить правильный PhatomData<..> чтоб поведение было таким как ты хочешь (чаще всего covariant)
ну и примеры функций чекеров (то что они скомпилируются уже будет гарантировать что в типах правильное предположение о variance)
возьмем два лайфтайма 'long:'short
fn covariant<'short, 'long: 'short>(
value: Foo<'long>,
) -> Foo<'short> {
value
}
fn contravariant<'short, 'long: 'short>(
value: Baz<'short>,
) -> Baz<'long> {
value
}
для инвариантного Bar ниодна из них не скомпилируется
https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=736049d0b7740534597e2c251762e1b3