Ricerca…
Sintassi
- const IDENTIFIER: type = constexpr;
- static [mut] IDENTIFIER: type = expr;
- lazy_static! {ID Ref ID statico: type = expr; }
Osservazioni
-
const
valoriconst
sono sempre in linea e non hanno indirizzo in memoria. -
static
valoristatic
non sono mai in linea e hanno un'istanza con un indirizzo fisso. -
static mut
valoristatic mut
non sono sicuri per la memoria e quindi sono accessibili solo in un bloccounsafe
. - A volte l'uso di variabili mutabili statiche globali nel codice multi-thread può essere pericoloso, quindi considera l'utilizzo di std :: sync :: Mutex o di altre alternative
-
lazy_static
oggettilazy_static
sono immutabili, vengono inizializzati una sola volta, sono condivisi tra tutti i thread e possono essere accessibili direttamente (non sono coinvolti tipi di wrapper). Al contrario,thread_local
oggettithread_local
sono pensati per essere mutabili, sono inizializzati una volta per ogni thread e gli accessi sono indiretti (che coinvolgono il tipo di wrapperLocalKey<T>
)
const
La parola chiave const
dichiara un binding costante globale.
const DEADBEEF: u64 = 0xDEADBEEF;
fn main() {
println("{:X}", DEADBEEF);
}
Questo produce
DEADBEEF
Statico
La parola chiave static
dichiara un binding statico globale, che può essere mutabile.
static HELLO_WORLD: &'static str = "Hello, world!";
fn main() {
println("{}", HELLO_WORLD);
}
Questo produce
Hello, world!
lazy_static!
Utilizzare la gabbia lazy_static
per creare variabili immutabili globali che vengono inizializzate in fase di runtime. Usiamo HashMap
come dimostrazione.
In Cargo.toml
:
[dependencies]
lazy_static = "0.1.*"
In 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());
}
Oggetti locali del thread
Un oggetto locale thread viene inizializzato al primo utilizzo in un thread. E come suggerisce il nome, ogni thread riceverà una nuova copia indipendente da altri thread.
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());
});
}
Uscite:
inner: 1
main: 3
Mut statico sicuro con mut_static
Gli elementi globali mutabili (chiamati static mut
, che evidenziano la contraddizione intrinseca implicata nel loro uso) non sono sicuri perché è difficile per il compilatore assicurarsi che vengano utilizzati in modo appropriato.
Tuttavia, l'introduzione di blocchi che si escludono a vicenda attorno ai dati consente di rendere globali le variabili mutabili. Questo NON significa che sono logicamente sicuri, però!
#[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);
}
}
Questo codice produce l'output:
Before mut: 0
Changed value to 3.
After mut: 3
After foo: 4
Questo non è qualcosa che dovrebbe accadere normalmente in Rust. foo()
non ha preso un riferimento mutabile a qualcosa, quindi non dovrebbe aver mutato nulla, eppure lo ha fatto. Questo può portare a errori di logica molto difficili da eseguire.
D'altra parte, a volte questo è esattamente quello che vuoi. Ad esempio, molti motori di gioco richiedono una cache globale di immagini e altre risorse caricate pigramente (o che utilizzano altre strategie di caricamento complesse) - MutStatic è perfetto per questo scopo.