% const
и static
В Rust можно определить постоянную с помощью ключевого слова const
:
#![allow(unused)] fn main() { const N: i32 = 5; }
В отличие от обычных имён, объявляемых с помощью let
, тип постоянной
надо указывать всегда.
Постоянные живут в течение всего времени работы программы. А именно, у них вообще нет определённого адреса в памяти. Это потому, что они встраиваются (inline) в каждое место, где есть их использование. По этой причине ссылки на одну и ту же постоянную не обязаны указывать на один и тот же адрес в памяти.
static
В Rust также можно объявить что-то вроде «глобальной переменной», используя статические значения. Они похожи на постоянные, но статические значения не встраиваются в место их использования. Это значит, что каждое значение существует в единственном экземпляре, и у него есть определённый адрес.
Вот пример:
#![allow(unused)] fn main() { static N: i32 = 5; }
Так же, как и в случае с постоянными, тип статического значения надо указывать всегда.
Статические значения живут в течение всего времени работы программы, и любая
ссылка на постоянную имеет статическое время жизни (static
lifetime):
#![allow(unused)] fn main() { static NAME: &'static str = "Steve"; }
Изменяемость
Вы можете сделать статическое значение изменяемым с помощью ключевого слова
mut
:
#![allow(unused)] fn main() { static mut N: i32 = 5; }
Поскольку N
изменяемо, один поток может изменить его во время того, как другой
читает его значение. Это ситуация «гонки» по данным, и она считается
небезопасным поведением в Rust. Поэтому и чтение, и изменение статического
изменяемого значения (static mut
) является небезопасным (unsafe), и
обе эти операции должны выполняться в небезопасных блоках (unsafe
block):
#![allow(unused)] fn main() { static mut N: i32 = 5; unsafe { N += 1; println!("N: {}", N); } }
Более того, любой тип, хранимый в статической переменной, должен быть ограничен
Sync
и не может иметь реализации Drop
.
Инициализация
И постоянные, и статические значения имеют определённые требования к тому, что можно хранить в них. Они могут быть проинициализированы только выражением, значение которого постоянно. Другими словами, вы не можете использовать вызов функции или что-то, вычисляемое во время исполнения.
Какую конструкцию стоит использовать?
Почти всегда стоит предпочитать постоянные. Ситуация, когда вам нужно реальное место в памяти и соответствующий ему адрес довольно редка. А использование постоянных позволяет компилятору провести оптимизации вроде распространения постоянных (constant propagation) не только в вашем контейнере, но и в тех, которые зависят от него.