rustclosuresglobal-variables

Dealing with so-called global variables in Rust


We all know that using global variables can lead to subtle bugs. I need to migrate Python programs to Rust, keeping the algorithm intact as far as possible. Once I have demonstrated Python-Rust equivalence there will be opportunities to debug and change the logic to fit Rust better. Here is a simple Python program using global variables, followed by my unsuccessful Rust version.

# global variable 
a = 15

# function to perform addition 
def add(): 
    global a
    a += 100

# function to perform subtraction
def subtract(): 
    global a
    a -= 100

# Using a global through functions
print("Initial value of a  = ", a)
add() 
print("a after addition   = ", a)
subtract() 
print("a after subtraction = ", a)

Here is a Rust program that runs, but I cannot get the closures to update the so-called global variable.

fn fmain() {
// global variable 
    let mut a = 15;

// perform addition 
    let add = || {
        let mut _name = a;
//        name += 100;  // the program won't compile if this is uncommented
    };

    call_once(add);

//  perform subtraction
    let subtract = || {
        let mut _name = a;
//        name -= 100;  // the program won't compile if this is uncommented
    };

    call_once(subtract);

    // Using a global through functions
    println!("Initial value of a    = {}", a);
    add();
    println!("a after addition      = {}", a);
    subtract();
    println!("a after subtraction   = {}", a);
}

fn main() {
    fmain();   
}

fn call_once<F>(f: F)
where
    F: FnOnce(),
{
    f();
}

My request: Re-create the Python logic in Rust.


Solution

  • Your Rust code is not using global variables, the a variable is stack-allocated. While Rust doesn't particularly endorse global variables, you can certainly use them. Translated to Rust that uses actual globals, your program would look like this:

    use std::sync::Mutex;
    
    // global variable
    static A: Mutex<u32> = Mutex::new(15);
    
    // function to perform addition
    fn add() {
        *A.lock().unwrap() += 100;
    }
    
    // function to perform subtraction
    fn subtract() {
        *A.lock().unwrap() -= 100;
    }
    
    fn main() {
        // Using a global through functions
        println!("Initial value of a  = {}", A.lock().unwrap());
        add();
        println!("a after addition    = {}", A.lock().unwrap());
        subtract();
        println!("a after subtraction = {}", A.lock().unwrap());
    }
    

    Playground

    If you prefer to use closures, you can do that too, but you'll need to use interior mutability to allow multiple closures to capture the same environment. For example, you could use a Cell:

    use std::cell::Cell;
    
    fn main() {
        let a = Cell::new(15);
        let add = || {
            a.set(a.get() + 100);
        };
        let subtract = || {
            a.set(a.get() - 100);
        };
    
        // Using a global through functions
        println!("Initial value of a    = {}", a.get());
        add();
        println!("a after addition      = {}", a.get());
        subtract();
        println!("a after subtraction   = {}", a.get());
    }
    

    Playground