Поиск…
Синтаксис
- const IDENTIFIER: type = constexpr;
- static [mut] IDENTIFIER: type = expr;
- lazy_static! {static ref IDENTIFIER: type = expr; }
замечания
-
const
значения всегда встраиваемые и не имеют адресов в памяти. -
static
значения никогда не привязаны и имеют один экземпляр с фиксированным адресом. -
static mut
values не являются безопасными для памяти и поэтому могут быть доступны только вunsafe
блоке. - Иногда использование глобальных статических изменяемых переменных в многопоточном коде может быть опасным, поэтому рассмотрите использование std :: sync :: Mutex или других альтернатив
- Объекты
lazy_static
неизменяемы, инициализируются только один раз, распределяются между всеми потоками и могут быть напрямую доступны (нет связанных типов обертки). Напротив, объектыthread_local
предназначены для изменения, инициализируются один раз для каждого потока, а обращения являются косвенными (с использованием типа оболочкиLocalKey<T>
)
Const
Ключевое слово const
объявляет глобальную привязку константы.
const DEADBEEF: u64 = 0xDEADBEEF;
fn main() {
println("{:X}", DEADBEEF);
}
Эти результаты
DEADBEEF
статический
static
ключевое слово объявляет глобальную статическую привязку, которая может быть изменчивой.
static HELLO_WORLD: &'static str = "Hello, world!";
fn main() {
println("{}", HELLO_WORLD);
}
Эти результаты
Hello, world!
lazy_static!
Используйте ящик lazy_static
для создания глобальных неизменяемых переменных, которые инициализируются во время выполнения. Мы используем HashMap
в качестве демонстрации.
В Cargo.toml
:
[dependencies]
lazy_static = "0.1.*"
В main.rs
:
#[macro_use]
extern crate lazy_static;
lazy_static! {
static ref HASHMAP: HashMap<u32, &'static str> = {
let mut m = HashMap::new();
m.insert(0, "hello");
m.insert(1, ",");
m.insert(2, " ");
m.insert(3, "world");
m
};
static ref COUNT: usize = HASHMAP.len();
}
fn main() {
// We dereference COUNT because it's type is &usize
println!("The map has {} entries.", *COUNT);
// Here we don't dereference with * because of Deref coercions
println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap());
}
Потоковые локальные объекты
Локальный объект потока инициализируется при первом использовании в потоке. И, как следует из названия, каждый поток будет получать новую копию, независимую от других потоков.
use std::cell::RefCell;
use std::thread;
thread_local! {
static FOO: RefCell<f32> = RefCell::new(1.0);
}
// When this macro expands, `FOO` gets type `thread::LocalKey<RefCell<f32>>`.
//
// Side note: One of its private member is a pointer to a function which is
// responsible for returning the thread-local object. Having all its members
// `Sync` [0], `LocalKey` is also implicitly `Sync`.
//
// [0]: As of writing this, `LocalKey` just has 2 function-pointers as members
fn main() {
FOO.with(|foo| {
// `foo` is of type `&RefCell<f64>`
*foo.borrow_mut() = 3.0;
});
thread::spawn(move|| {
// Note that static objects do not move (`FOO` is the same everywhere),
// but the `foo` you get inside the closure will of course be different.
FOO.with(|foo| {
println!("inner: {}", *foo.borrow());
});
}).join().unwrap();
FOO.with(|foo| {
println!("main: {}", *foo.borrow());
});
}
Выходы:
inner: 1
main: 3
Безопасный статический мут с mut_static
Смещаемые глобальные элементы (называемые static mut
, выделяющие неотъемлемое противоречие, связанное с их использованием) являются небезопасными, поскольку компилятору сложно обеспечить их надлежащее использование.
Тем не менее, введение взаимоисключающих блокировок вокруг данных позволяет безопасные для памяти изменяемые глобальные переменные. Это НЕ означает, что они логически безопасны!
#[macro_use]
extern crate lazy_static;
extern crate mut_static;
use mut_static::MutStatic;
pub struct MyStruct { value: usize }
impl MyStruct {
pub fn new(v: usize) -> Self{
MyStruct { value: v }
}
pub fn getvalue(&self) -> usize { self.value }
pub fn setvalue(&mut self, v: usize) { self.value = v }
}
lazy_static! {
static ref MY_GLOBAL_STATE: MutStatic<MyStruct> = MutStatic::new();
}
fn main() {
// Here, I call .set on the MutStatic to put data inside it.
// This can fail.
MY_GLOBAL_STATE.set(MyStruct::new(0)).unwrap();
{
// Using the global state immutably is easy...
println!("Before mut: {}",
MY_GLOBAL_STATE.read().unwrap().getvalue());
}
{
// Using it mutably is too...
let mut mut_handle = MY_GLOBAL_STATE.write().unwrap();
mut_handle.setvalue(3);
println!("Changed value to 3.");
}
{
// As long as there's a scope change we can get the
// immutable version again...
println!("After mut: {}",
MY_GLOBAL_STATE.read().unwrap().getvalue());
}
{
// But beware! Anything can change global state!
foo();
println!("After foo: {}",
MY_GLOBAL_STATE.read().unwrap().getvalue());
}
}
// Note that foo takes no parameters
fn foo() {
let val;
{
val = MY_GLOBAL_STATE.read().unwrap().getvalue();
}
{
let mut mut_handle =
MY_GLOBAL_STATE.write().unwrap();
mut_handle.setvalue(val + 1);
}
}
Этот код выводит результат:
Before mut: 0
Changed value to 3.
After mut: 3
After foo: 4
Это не то, что должно произойти в Rust. foo()
не принимала изменчивую ссылку на что-либо, поэтому она не должна была ничего мутировать, и все же она сделала это. Это может привести к очень трудным для отладки логических ошибок.
С другой стороны, это иногда то, что вы хотите. Например, для многих игровых движков требуется глобальный кэш изображений и других ресурсов, которые лениво загружаются (или используют какую-то другую сложную стратегию загрузки). MutStatic идеально подходит для этой цели.