Статические элементы
Syntax
StaticItem →
ItemSafety?1 static mut? IDENTIFIER : Type ( = Expression )? ;
Статический элемент похож на константу, за исключением того, что он представляет выделение памяти в программе, которое инициализируется выражением инициализатора. Все ссылки и сырые указатели на статическую переменную ссылаются на одно и то же выделение памяти.
Статические элементы имеют время жизни static, которое переживает все другие времена жизни в программе Rust.
Статические элементы не вызывают drop в конце программы.
Если static имеет размер не менее 1 байта, это выделение памяти не пересекается со всеми другими такими
выделениями static, а также с выделениями в куче и переменными в стеке. Однако хранилище
неизменяемых static элементов может перекрываться с выделениями, которые сами не имеют уникального адреса, таких как
продвинутые значения и элементы const.
Объявление static определяет статическое значение в пространстве имён значений модуля или блока, где оно находится.
Статический инициализатор является константным выражением, вычисляемым во время компиляции. Статические инициализаторы могут ссылаться на другие статические переменные и читать их. При чтении из изменяемых статических переменных они читают начальное значение этой статической переменной.
Не-mut статические элементы, которые содержат тип, не являющийся внутренне изменяемым, могут
быть размещены в памяти только для чтения.
Весь доступ к статической переменной безопасен, но существует ряд ограничений на статические переменные:
- Тип должен иметь ограничение трейта
Sync, чтобы разрешить потокобезопасный доступ.
Выражение инициализатора должно быть опущено во внешнем блоке и должно быть предоставлено для свободных статических элементов.
Квалификаторы safe и unsafe семантически разрешены только при использовании во внешнем блоке.
Статические переменные и обобщения
Статический элемент, определённый в обобщённой области видимости (например, в общем или стандартном реализации), приведёт к определению ровно одного статического элемента, как если бы статическое определение было вынесено из текущей области видимости в модуль. НЕ будет одного элемента на каждую мономорфизацию.
Этот код:
use std::sync::atomic::{AtomicUsize, Ordering}; trait Tr { fn default_impl() { static COUNTER: AtomicUsize = AtomicUsize::new(0); println!("default_impl: counter was {}", COUNTER.fetch_add(1, Ordering::Relaxed)); } fn blanket_impl(); } struct Ty1 {} struct Ty2 {} impl<T> Tr for T { fn blanket_impl() { static COUNTER: AtomicUsize = AtomicUsize::new(0); println!("blanket_impl: counter was {}", COUNTER.fetch_add(1, Ordering::Relaxed)); } } fn main() { <Ty1 as Tr>::default_impl(); <Ty2 as Tr>::default_impl(); <Ty1 as Tr>::blanket_impl(); <Ty2 as Tr>::blanket_impl(); }
выводит
default_impl: counter was 0
default_impl: counter was 1
blanket_impl: counter was 0
blanket_impl: counter was 1
Изменяемые статические переменные
Если статический элемент объявлен с ключевым словом mut, то программе разрешено
его изменять. Одна из целей Rust - сделать ошибки параллелизма трудными
для возникновения, и это, очевидно, очень большой источник состояний гонки или
других ошибок.
По этой причине unsafe блок требуется при чтении
или записи изменяемой статической переменной. Следует позаботиться о том, чтобы
изменения изменяемой статической переменной были безопасны по отношению к другим потокам,
выполняющимся в том же процессе.
Тем не менее, изменяемые статические переменные всё ещё очень полезны. Они могут использоваться с библиотеками C
и также могут быть привязаны из библиотек C в блоке extern.
#![allow(unused)] fn main() { fn atomic_add(_: *mut u32, _: u32) -> u32 { 2 } static mut LEVELS: u32 = 0; // Это нарушает идею отсутствия разделяемого состояния, и это внутренне не // защищает от состояний гонки, поэтому эта функция `unsafe` unsafe fn bump_levels_unsafe() -> u32 { unsafe { let ret = LEVELS; LEVELS += 1; return ret; } } // В качестве альтернативы `bump_levels_unsafe`, эта функция безопасна, предполагая, // что у нас есть функция atomic_add, которая возвращает старое значение. Эта // функция безопасна только если никакой другой код не обращается к статической переменной неатомарным // способом. Если такие обращения возможны (как в `bump_levels_unsafe`), // то это должно быть `unsafe`, чтобы указать вызывающей стороне, что они // всё ещё должны защищаться от параллельного доступа. fn bump_levels_safe() -> u32 { unsafe { return atomic_add(&raw mut LEVELS, 1); } } }
Изменяемые статические переменные имеют те же ограничения, что и обычные статические переменные, за исключением того, что
тип не должен реализовывать трейт Sync.
Использование статических переменных или констант
Может быть confusing, следует ли использовать элемент constant или элемент static. Константы, как правило, следует предпочитать над статическими переменными, если только одно из следующих не верно:
- Хранятся большие объёмы данных.
- Требуется свойство единственного адреса статических переменных.
- Требуется внутренняя изменяемость.
-
Квалификаторы функций
safeиunsafeсемантически разрешены только внутри блоковextern. ↩