Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use global variables in Rust?

People also ask

Can you use global variables?

You should typically not use global variables unless absolutely necessary because global variables are only cleaned up when explicitly told to do so or your program ends. If you are running a multi-threaded application, multiple functions can write to the variable at the same time.

Can global variables be used anywhere?

Global variables have file scope (also informally called global scope or global namespace scope), which means they are visible from the point of declaration until the end of the file in which they are declared. Once declared, a global variable can be used anywhere in the file from that point onward!

Does Rust have static variables?

Static variables can be mutable. Since Rust can't prove the absence of data races when accessing a static mutable variable, accessing it is unsafe.

How do you make a variable in Rust?

Declaring a variable In Rust, we can create variables by assigning data to alphanumeric words( excluding keywords) using the let keyword or const keyword. Syntax: let x = "Value"; or const x = "Value"; Example: Rust.


It's possible, but heap allocation is not allowed directly. Heap allocation is performed at runtime. Here are a few examples:

static SOME_INT: i32 = 5;
static SOME_STR: &'static str = "A static string";
static SOME_STRUCT: MyStruct = MyStruct {
    number: 10,
    string: "Some string",
};
static mut db: Option<sqlite::Connection> = None;

fn main() {
    println!("{}", SOME_INT);
    println!("{}", SOME_STR);
    println!("{}", SOME_STRUCT.number);
    println!("{}", SOME_STRUCT.string);

    unsafe {
        db = Some(open_database());
    }
}

struct MyStruct {
    number: i32,
    string: &'static str,
}

You can use static variables fairly easily as long as they are thread-local.

The downside is that the object will not be visible to other threads your program might spawn. The upside is that unlike truly global state, it is entirely safe and is not a pain to use - true global state is a massive pain in any language. Here's an example:

extern mod sqlite;

use std::cell::RefCell;

thread_local!(static ODB: RefCell<sqlite::database::Database> = RefCell::new(sqlite::open("test.db"));

fn main() {
    ODB.with(|odb_cell| {
        let odb = odb_cell.borrow_mut();
        // code that uses odb goes here
    });
}

Here we create a thread-local static variable and then use it in a function. Note that it is static and immutable; this means that the address at which it resides is immutable, but thanks to RefCell the value itself will be mutable.

Unlike regular static, in thread-local!(static ...) you can create pretty much arbitrary objects, including those that require heap allocations for initialization such as Vec, HashMap and others.

If you cannot initialize the value right away, e.g. it depends on user input, you may also have to throw Option in there, in which case accessing it gets a bit unwieldy:

extern mod sqlite;

use std::cell::RefCell;

thread_local!(static ODB: RefCell<Option<sqlite::database::Database>> = RefCell::New(None));

fn main() {
    ODB.with(|odb_cell| {
        // assumes the value has already been initialized, panics otherwise
        let odb = odb_cell.borrow_mut().as_mut().unwrap();
        // code that uses odb goes here
    });
}

Look at the const and static section of the Rust book.

You can use something like the following:

const N: i32 = 5;

or

static N: i32 = 5;

in the global space.

But these are not mutable. For mutability, you could use something like:

static mut N: i32 = 5;

Then reference them like:

unsafe {
    N += 1;

    println!("N: {}", N);
}

I am new to Rust, but this solution seems to work:

#[macro_use]
extern crate lazy_static;

use std::sync::{Arc, Mutex};

lazy_static! {
    static ref GLOBAL: Arc<Mutex<GlobalType> =
        Arc::new(Mutex::new(GlobalType::new()));
}

Another solution is to declare a crossbeam channel transmit/receive pair as an immutable global variable. The channel should be bounded and can only hold one element. When you initialize the global variable, push the global instance into the channel. When using the global variable, pop the channel to acquire it and push it back when done using it.

Both solutions should provide a safe approach to using global variables.


Heap allocations are possible for static variables if you use the lazy_static macro as seen in the documentation:

Using this macro, it is possible to have statics that require code to be executed at runtime in order to be initialized. This includes anything requiring heap allocations, like vectors or hash maps, as well as anything that requires function calls to be computed.

// Declares a lazily evaluated constant HashMap. The HashMap will be evaluated once and
// stored behind a global static reference.

use lazy_static::lazy_static;
use std::collections::HashMap;

lazy_static! {
    static ref PRIVILEGES: HashMap<&'static str, Vec<&'static str>> = {
        let mut map = HashMap::new();
        map.insert("James", vec!["user", "admin"]);
        map.insert("Jim", vec!["user"]);
        map
    };
}

fn show_access(name: &str) {
    let access = PRIVILEGES.get(name);
    println!("{}: {:?}", name, access);
}

fn main() {
    let access = PRIVILEGES.get("James");
    println!("James: {:?}", access);

    show_access("Jim");
}