Ответ 1
Ответ без ответа
Избегайте глобального состояния в целом. Вместо этого создайте объект где-нибудь раньше (возможно, в main
), а затем передайте изменяемые ссылки на этот объект в нужные ему места. Это, как правило, делает ваш код более легким для рассуждения и не требует столь большого наклона назад.
Посмотрите на себя в зеркало, прежде чем решите, что хотите глобальные изменяемые переменные. Есть редкие случаи, когда это полезно, поэтому почему стоит знать, как это сделать.
Еще хотите сделать один...?
Использование lazy-static
Ящик lazy-static может отнять часть тяжелой работы по созданию синглета (ниже). Вот глобальный изменяемый вектор:
#[macro_use]
extern crate lazy_static;
use std::sync::Mutex;
lazy_static! {
static ref ARRAY: Mutex<Vec<u8>> = Mutex::new(vec![]);
}
fn do_a_call() {
ARRAY.lock().unwrap().push(1);
}
fn main() {
do_a_call();
do_a_call();
do_a_call();
println!("called {}", ARRAY.lock().unwrap().len());
}
Ручная реализация
Это сильно отличается от реализации Rust 1.0 stdin
. Я прокомментировал строку с тем, что делает каждая строка.
use std::sync::{Arc, Mutex, Once, ONCE_INIT};
use std::time::Duration;
use std::{mem, thread};
#[derive(Clone)]
struct SingletonReader {
// Since we will be used in many threads, we need to protect
// concurrent access
inner: Arc<Mutex<u8>>
}
fn singleton() -> SingletonReader {
// Initialize it to a null value
static mut SINGLETON: *const SingletonReader = 0 as *const SingletonReader;
static ONCE: Once = ONCE_INIT;
unsafe {
ONCE.call_once(|| {
// Make it
let singleton = SingletonReader {
inner: Arc::new(Mutex::new(0))
};
// Put it in the heap so it can outlive this call
SINGLETON = mem::transmute(Box::new(singleton));
});
// Now we give out a copy of the data that is safe to use concurrently.
(*SINGLETON).clone()
}
}
fn main() {
// Let use the singleton in a few threads
let threads: Vec<_> = (0..10).map(|i| {
thread::spawn(move || {
thread::sleep(Duration::from_millis(i * 10));
let s = singleton();
let mut data = s.inner.lock().unwrap();
*data = i as u8;
})
}).collect();
// And let check the singleton every so often
for _ in 0u8..20 {
thread::sleep(Duration::from_millis(5));
let s = singleton();
let data = s.inner.lock().unwrap();
println!("It is: {}", *data);
}
for thread in threads.into_iter() {
thread.join().unwrap();
}
}
Это выдает:
It is: 0
It is: 1
It is: 1
It is: 2
It is: 2
It is: 3
It is: 3
It is: 4
It is: 4
It is: 5
It is: 5
It is: 6
It is: 6
It is: 7
It is: 7
It is: 8
It is: 8
It is: 9
It is: 9
It is: 9
Этот код компилируется с помощью Rust 1.9, но обратите внимание, что выделенная память просочилась...
Действительно, вы, вероятно, захотите сделать SingletonReader
реализовать Deref
и DerefMut
, поэтому вам не пришлось втыкать в объект и блокировать его самостоятельно.