Recherche…
Syntaxe
- const IDENTIFIER: type = constexpr;
- static [mut] IDENTIFICATEUR: type = expr;
- lazy_static! {static ref IDENTIFIER: type = expr; }
Remarques
-
const
valeursconst
sont toujours en ligne et n'ont pas d'adresse en mémoire. -
static
valeursstatic
ne sont jamais intégrées et comportent une instance avec une adresse fixe. -
static mut
valeurs destatic mut
ne sont pas sûres pour la mémoire et ne peuvent donc être accédées que dans un blocunsafe
. - Parfois, l'utilisation de variables mutables statiques globales dans du code multithread peut être dangereuse, alors envisagez d'utiliser std :: sync :: Mutex ou d'autres alternatives
-
lazy_static
objetslazy_static
sont immuables, ne sont initialisés qu'une seule fois, sont partagés entre tous les threads et peuvent être directement accessibles (il n'y a pas de type de wrapper). En revanche, les objetsthread_local
sont censés être mutables, sont initialisés une fois pour chaque thread et les accès sont indirects (impliquant le type de wrapperLocalKey<T>
)
Const
Le mot-clé const
déclare une liaison de constante globale.
const DEADBEEF: u64 = 0xDEADBEEF;
fn main() {
println("{:X}", DEADBEEF);
}
Cette sorties
DEADBEEF
Statique
Le mot-clé static
déclare une liaison statique globale, qui peut être mutable.
static HELLO_WORLD: &'static str = "Hello, world!";
fn main() {
println("{}", HELLO_WORLD);
}
Cette sorties
Hello, world!
lazy_static!
Utilisez la caisse lazy_static
pour créer des variables immuables globales qui sont initialisées à l'exécution. Nous utilisons HashMap
comme démonstration.
Dans Cargo.toml
:
[dependencies]
lazy_static = "0.1.*"
En 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());
}
Thread-local Objects
Un objet thread-local est initialisé lors de sa première utilisation dans un thread. Et comme son nom l'indique, chaque thread aura une nouvelle copie indépendante des autres threads.
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());
});
}
Les sorties:
inner: 1
main: 3
Mut statique sûr avec mut_static
Les éléments globaux mutables (appelés static mut
, mettant en évidence la contradiction inhérente à leur utilisation) sont dangereux car il est difficile pour le compilateur de s’assurer qu’ils sont utilisés correctement.
Cependant, l'introduction de verrous mutuellement exclusifs autour des données permet des globaux mutables sécurisés par la mémoire. Cela ne signifie pas qu'ils sont logiquement sûrs, cependant!
#[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);
}
}
Ce code produit la sortie:
Before mut: 0
Changed value to 3.
After mut: 3
After foo: 4
Ce n'est pas quelque chose qui devrait arriver à Rust normalement. foo()
n'a pas pris de référence mutable, alors ça n'aurait pas dû faire de mutations, et pourtant il l'a fait. Cela peut rendre très difficile le débogage des erreurs de logique.
D'un autre côté, c'est parfois exactement ce que vous voulez. Par exemple, de nombreux moteurs de jeu nécessitent un cache global d’images et d’autres ressources qui est chargé paresseusement (ou utilise une autre stratégie de chargement complexe) - MutStatic est parfait pour cela.