Работа с глобальными переменными в Rust
Глобальные переменные в Раст объявляются так: static ИМЯ:тип=значение. Например: static mut COUNT:i32=0; Но если ми попробуем объявить вектор static mut MYVECT:Vec
, то получим ошибку при компиляции: «calls in statics are limited to struct and enum constructors”
Попытка сделать так static mut MYVECT: Option<Vec<i32>>=None; вызовет другую ошибку “destructors in statics are an unstable feature” и отправит нас искать счастья в ночные сборки. Так было в Rust 1.13, такая же ситуация и сейчас в Rust 1.15. Для решения этой проблемы есть нескоько способов.
Способ первый- Ленивая инициализация с помощью макроса lazy_static! с использованием Mutex.
#[macro_use] extern crate lazy_static; use std::sync:: {Mutex,RwLock}; lazy_static! { pub static ref MUTEX_VEC:Mutex<Vec<u32>>= Mutex::new(Vec::new()); } fn main() { let mut v1=MUTEX_VEC.lock().unwrap(); *v1 =vec!(1,2,3); v1.push(4); println!("{:?}",*v1); }Этот вариант компилируется и работает. А теперь организуем дедлок под Windows))
let mut v1=MUTEX_VEC.lock().unwrap(); *v1 =vec!(1,2,3); println!("{:?}",MUTEX_VEC.lock().unwrap()[0]);Все чудесно компилируется и висит)) Конечно, рабочий вариант такой:
{ let mut v1=MUTEX_VEC.lock().unwrap(); *v1 =vec!(1,2,3); } println!("{:?}",MUTEX_VEC.lock().unwrap()[0]);Нужно освободить захваченный ресурс. Для этого переменная v1 должна выйти из области видимости. Другой способ - это использование try_lock вместо lock:
let mut v1=MUTEX_VEC.lock().unwrap(); *v1 =vec!(1,2,3); match MUTEX_VEC.try_lock(){ Ok(v)=>println!("{}",v[0]), Err(_)=>println!("Busy") }или на комбинаторах:
let mut v1=MUTEX_VEC.lock().unwrap(); *v1 =vec!(1,2,3); MUTEX_VEC.try_lock().map(|v| println!("{:?}",v[0])).map_err(|_|println!("Busy"));Оба варианта напечатают Busy. Переменная v1 еще в области видимости. А вот вариант
*MUTEX_VEC.lock().unwrap()=vec!(1,2,3); MUTEX_VEC.try_lock().map(|v| println!("{:?}",v[0])).map_err(|_|println!("Busy"));напечатает 1. Ресурс свободен после первой строчки кода.
Способ второй- Ленивая инициализация с помощью макроса lazy_static! с использованием RwLock вместо Mutex.
lazy_static! { pub static ref RWLOCK_VEC: RwLock<Vec<u32>> = RwLock::new(Vec::new()); } fn main() { { let mut v1 = RWLOCK_VEC.write().unwrap(); *v1 = vec!(1, 2, 3); v1.push(4); } println!("{:?}", RWLOCK_VEC.read().unwrap()[0]); }Практически такие же проблемы и пути их решения. Но чтение, RWLOCK_VEC.read() не блокирует ресурс. И read() значительно быстрее чем lock(). Нужно отметить, что lazy_static! это внешний крейт и его нужно добавить в Cargo.toml так [dependencies] lazy_static = "0.1.*"
Третий вариант — также ленивая инициализация но с помощью макроса thread_local! , который входит в стандартную библиотеку, и использование RefCell.
use std::cell::RefCell; thread_local! { pub static REFCELL_VEC: RefCell<Vec<u32>> = RefCell::new(Vec::new()); } fn main() { REFCELL_VEC.with(|c| { let mut v1=c.borrow_mut(); *v1 =vec!(1,2,3); v1.push(4); println!("{:?}",*v1); }); }но такой код запаникует:
REFCELL_VEC.with(|c| { let mut v1=c.borrow_mut(); *v1 =vec!(1,2,3); println!("{:?}",c.borrow()[0]);thread 'main' panicked at 'already mutably borrowed: BorrowError' а такой будет работать нормально:
REFCELL_VEC.with(|c| { { let mut v1 = c.borrow_mut(); *v1 = vec!(1, 2, 3); } println!("{:?}",c.borrow()[0]); });На этот раз нарушены правила заимствования переменной с. Вместо borrow() и borrow_mut() возможно использование try_borrow() и try_borrow_mut()
REFCELL_VEC.with(|c| { let mut v1 = c.borrow_mut(); *v1 = vec!(1, 2, 3); match c.try_borrow(){ Ok(v)=>println!("{}",v[0]), Err(_)=>println!("Busy") } )};или на комбинаторах:
… c.try_borrow().map(|v|println!("{}",v[0])).map_err(|_| println!("Busy")); …Есть и вариант с проверкой возможности заимствования:
… if c.try_borrow().is_ok() { println!("{}",c.borrow()[0]) } else { println!("Busy") } …Самым быстрым и правильным вариантом, если не нужна многопоточность, конечно является третий.
Комментарии
Отправить комментарий