可以在 Rust 中使用全局变量吗?

我知道在一般情况下,全局变量是要避免的。尽管如此,我认为在实际意义上,有时候使用它们是可取的(在变量是程序不可分割的情况下)。

为了学习 Rust,我目前正在 GitHub 上使用 sqlite3和 Rust/sqlite3包编写一个数据库测试程序。因此,这就需要(在我的测试程序中)(作为全局变量的替代品)在大约有12个函数的函数之间传递数据库变量。下面是一个例子。

  1. 在 Rust 中使用全局变量是否可行和可取?

  2. 给定下面的例子,我可以声明和使用一个全局变量吗?

extern crate sqlite;


fn main() {
let db: sqlite::Connection = open_database();


if !insert_data(&db, insert_max) {
return;
}
}

我尝试了以下方法,但似乎不太正确,导致了下面的错误(我还尝试了 unsafe块) :

extern crate sqlite;


static mut DB: Option<sqlite::Connection> = None;


fn main() {
DB = sqlite::open("test.db").expect("Error opening test.db");
println!("Database Opened OK");


create_table();
println!("Completed");
}


// Create Table
fn create_table() {
let sql = "CREATE TABLE IF NOT EXISTS TEMP2 (ikey INTEGER PRIMARY KEY NOT NULL)";
match DB.exec(sql) {
Ok(_) => println!("Table created"),
Err(err) => println!("Exec of Sql failed : {}\nSql={}", err, sql),
}
}

编译导致的错误:

error[E0308]: mismatched types
--> src/main.rs:6:10
|
6 |     DB = sqlite::open("test.db").expect("Error opening test.db");
|          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `std::option::Option`, found struct `sqlite::Connection`
|
= note: expected type `std::option::Option<sqlite::Connection>`
found type `sqlite::Connection`


error: no method named `exec` found for type `std::option::Option<sqlite::Connection>` in the current scope
--> src/main.rs:16:14
|
16 |     match DB.exec(sql) {
|              ^^^^
172796 次浏览

这是可能的,但是不允许直接分配堆。堆分配是在运行时执行的。下面是一些例子:

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,
}

看看 锈书的 ABC0和 static部分

您可以使用以下内容:

const N: i32 = 5;

或者

static N: i32 = 5;

在全球范围内。

但是这些是不可变的,对于可变性,你可以使用这样的东西:

static mut N: i32 = 5;

然后引用它们,比如:

unsafe {
N += 1;


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

只要静态变量是线程本地的,就可以很容易地使用它们

缺点是对象对程序可能产生的其他线程不可见。好的一面是,与真正的全球化状态不同,它是完全安全的,而且使用起来不痛苦——真正的全球化状态在任何语言中都是一种巨大的痛苦。这里有一个例子:

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
});
}

在这里,我们创建一个线程局部静态变量,然后在函数中使用它。请注意,它是静态的和不可变的; 这意味着它所驻留的地址是不可变的,但是由于 RefCell,值本身是可变的。

与常规的 static不同,在 thread-local!(static ...)中您可以创建几乎任意的对象,包括那些需要为初始化分配堆的对象,如 VecHashMap和其他对象。

如果你不能马上初始化这个值,例如它取决于用户输入,你可能还需要把 Option放进去,在这种情况下访问它会变得有点麻烦:

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
});
}

我对 Rust 还不熟悉,但这个方法似乎行得通:

#[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()));
}

另一种解决方案是将一个横梁信道发送/接收对声明为一个不变的全局变量。通道应该是有界的,并且只能容纳一个元素。初始化全局变量时,将全局实例推入通道。当使用全局变量时,弹出通道以获取它,并在使用它时将其推回。

这两种解决方案都应该提供一种使用全局变量的安全方法。

如果使用 < em > lazy _ static 宏,则可以为静态变量分配堆:

使用这个宏,可以拥有需要在运行时执行代码才能初始化的静态。这包括任何需要堆分配的内容,比如向量或散列映射,以及任何需要计算函数调用的内容。

// 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");
}

每晚使用 once_celllazy_staticSyncOnceCell板条箱。

enter image description here

我认为这一页涵盖了 https://www.sitepoint.com/rust-global-variables/的大部分方面