Работа с глобальными переменными в Rust



Глобальные переменные в Раст объявляются так: static ИМЯ:тип=значение. Например: static mut COUNT:i32=0; Но если ми попробуем объявить вектор static mut MYVECT:Vec=Vec::new();
, то получим ошибку при компиляции: «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")
}
…
Самым быстрым и правильным вариантом, если не нужна многопоточность, конечно является третий.

Комментарии

Популярные сообщения из этого блога

IUP - библиотека элементов GUI

Виртуальный GUI на Rust