Search code examples
ruststaticglobal-variables

Rust boxed closure in global variable


I'd like to store a boxed closure (Box<dyn Fn(f64, f64) -> f64>) in a global variable EXPRESSION_FUNCTION.

Here is my attempt at achieving this:

type F2d = Box<dyn Fn(f64, f64) -> f64>;

static mut EXPRESSION_FUNCTION: *mut F2d = 0 as *mut F2d;

fn create_function() -> F2d {
    Box::new(|_: f64, _: f64| 0.0)
}

fn set_expression_function() {
    unsafe {
        EXPRESSION_FUNCTION = Box::into_raw(create_function());
    }
}

I get a type mismatch:

error[E0308]: mismatched types
  --> src\lib.rs:79:45
   |
79 |         EXPRESSION_FUNCTION = Box::into_raw(create_function());
   |                                             ^^^^^^^^^^^^^^^^^ expected struct `Box`, found trait object `dyn Fn`
   |
   = note: expected struct `Box<Box<(dyn Fn(f64, f64) -> f64 + 'static)>, _>`
              found struct `Box<(dyn Fn(f64, f64) -> f64 + 'static), std::alloc::Global>`

How can I convert the result of create_function to fit into a global variable?


Solution

  • Do not use static mut. It is extremely easy to write incorrect programs that violate Rust's reference and aliasing rules using static mut. Instead, use static together with some kind of cell (interior-mutable type) — even UnsafeCell (which should be a last resort) is easier to use correctly than static mut.

    If your variable is going to be initialized only once, the simplest solution is to use once_cell (which is on its way to being included in the standard library, but for now is a widely used third-party library).

    use once_cell::sync::OnceCell;
    
    type F2d = Box<dyn Fn(f64, f64) -> f64 + Send + Sync>;
    
    static EXPRESSION_FUNCTION: OnceCell<F2d> = OnceCell::new();
    
    fn create_function() -> F2d {
        Box::new(|_: f64, _: f64| 2.0)
    }
    
    fn set_expression_function() {
        if let Err(_) = EXPRESSION_FUNCTION.set(create_function()) {
            panic!("already set");
        }
    }
    
    fn main() {
        set_expression_function();
        
        let f = EXPRESSION_FUNCTION.get().expect("not set");
        assert_eq!(f(10., 0.), 2.0);
    }
    

    If it needs to be reassignable, then you need a lock. parking_lot::RwLock is a nice choice because it can be constructed at compile time, so no lazy initialization is needed:

    use parking_lot::RwLock;
    
    type F2d = Box<dyn Fn(f64, f64) -> f64 + Send + Sync>;
    
    static EXPRESSION_FUNCTION: RwLock<Option<F2d>> = RwLock::new(None);
    
    fn create_function() -> F2d {
        Box::new(|_: f64, _: f64| 2.0)
    }
    
    fn set_expression_function() {
        *EXPRESSION_FUNCTION.write() = Some(create_function());
    }
    
    fn main() {
        set_expression_function();
        
        let option_fn_guard = EXPRESSION_FUNCTION.read();
        let f = option_fn_guard.as_ref().expect("not set");
        assert_eq!(f(10., 0.), 2.0);
    }
    

    Unlike the static mut you started with, both of these programs are sound (will not segfault, etc.) no matter how the functions and global variable are used. This is good Rust programming practice.