是否有可能在Rust中使用全局variables?
我知道,一般来说,全局variables是可以避免的。 尽pipe如此,我认为从实际意义上讲,使用它们有时是可取的(在variables是程序中不可或缺的部分)。
为了学习Rust,我正在使用sqlite3和GitHub上的Rust / sqlite3包编写一个数据库testing程序。 因此,必须(在我的testing程序中)(作为全局variables的一种替代方式),在大约十几个函数之间传递数据库variables。 下面是一个例子。
-
在Rust中使用全局variables是否可行也是可行的?
-
鉴于下面的例子,我可以声明和使用全局variables吗?
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) { | ^^^^
这是可能的,但不允许堆分配直接。 堆分配在运行时执行。 这里有几个例子:
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, }
看Rust书的const
和static
部分 。
你可以使用如下的东西:
const N: i32 = 5;
要么
static N: i32 = 5;
在全球空间。
但这些都不可变 对于可变性,你可以使用像这样的东西:
static mut N: i32 = 5;
然后引用他们:
unsafe { N += 1; println!("N: {}", N); }
只要它们是线程本地的,就可以使用静态variables。
缺点是该对象不会被其他线程看到你的程序可能产生。 好处在于,与真正的全球化国家不同,这是完全安全的,并不是一个痛苦的使用 – 真正的全球化国家是任何语言的巨大痛苦。 这是一个例子:
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 }); }
这里我们创build一个线程局部静态variables,然后在函数中使用它。 请注意,它是静态的和不可变的; 这意味着它所在的地址是不可变的,但是由于RefCell
的值本身是可变的。
与常规static
不同的是,在thread-local!(static ...)
,可以创build非常随意的对象,包括那些需要初始化的堆分配(如Vec
, HashMap
等)的对象。
如果你不能立即初始化这个值,例如它取决于用户的input,你也可能需要在那里抛出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 }); }
声明主要的variables。 那么,它将被视为一个全局variables。