我想在全局变量EXPRESSION_FUNCTION
中存储一个装箱闭包(Box<dyn Fn(f64, f64) -> f64>
(。
以下是我实现这一目标的尝试:
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());
}
}
我得到一个类型不匹配:
error[E0308]: mismatched types
--> srclib.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>`
如何将create_function
的结果转换为全局变量?
请勿使用static mut
使用static mut
非常容易编写违反Rust引用和别名规则的错误程序。相反,将static
与某种细胞(内部可变类型(一起使用——甚至UnsafeCell
(这应该是最后的手段(也比static mut
更容易正确使用。
如果您的变量只初始化一次,最简单的解决方案是使用once_cell
(它正在被包括在标准库中,但目前是一个广泛使用的第三方库(。
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);
}
如果它需要重新分配,那么你需要一个锁。parking_lot::RwLock
是一个不错的选择,因为它可以在编译时构建,所以不需要延迟初始化:
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);
}
与您开始使用的static mut
不同,无论函数和全局变量如何使用,这两个程序都是健全的(不会segfault等(。这是一个很好的Rust编程实践。
我使用Some
:使其工作
type F2d = Box<dyn Fn(f64, f64) -> f64>;
static mut EXPRESSION_FUNCTION: Option<F2d> = None;
fn create_function() -> F2d {
Box::new(|x: f64, y: f64| x+y)
}
fn set_expression_function() {
unsafe {
EXPRESSION_FUNCTION = Some(create_function());
}
}
fn main() {
set_expression_function();
unsafe {
if let Some(f) = &EXPRESSION_FUNCTION{
println!("{}", f(1.0, 2.0));
}
};
}