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

时间:2013-10-26 09:39:46

标签: global-variables rust

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

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

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

  2. 根据以下示例,我可以声明并使用全局变量吗?

  3. 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) {
       |              ^^^^
    

5 个答案:

答案 0 :(得分:33)

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

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

答案 1 :(得分:23)

只要它们是线程本地的,您就可以非常轻松地使用静态变量。

缺点是该程序可能产生的其他线程不会看到该对象。好处是,与真正的全球状态不同,它是完全安全的,并不是一种痛苦 - 真正的全球状态是任何语言的巨大痛苦。这是一个例子:

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等。< / p>

如果您无法立即初始化该值,例如它取决于用户输入,您可能还必须在其中抛出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
    });
}

答案 2 :(得分:14)

查看const and static section of the Rust book

您可以使用以下内容:

const N: i32 = 5; 

static N: i32 = 5;

在全球空间。

但这些都不可变。对于可变性,您可以使用类似:

static mut N: i32 = 5;

然后引用它们:

unsafe {
    N += 1;

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

答案 3 :(得分:1)

如果您使用lazy_static中的docs宏,则可以为静态变量进行堆分配

使用此宏,可能会有需要在运行时执行代码才能进行初始化的静态变量。这包括需要堆分配的所有内容,例如向量或哈希图,以及需要计算函数调用的所有内容。

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

答案 4 :(得分:-1)

我不知道为什么没人谈论使用Arc的解决方案。我还是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()));
}

另外,另一种解决方案是将交叉光束通道tx / rx对声明为不可变的全局变量。通道应该有界,并且只能容纳1个元素。初始化全局变量时,将全局实例推入通道。使用全局变量时,弹出通道以获取它,并在使用完后将其推回去。

两个解决方案都应提供Rust安全方法来使用全局变量。