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

Bri*_* Oh 78 global-variables rust

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

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

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

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

extern crate sqlite;

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

    if !insert_data(&db, insert_max) {
        return;
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试了以下方法,但它看起来不太正确并导致下面的错误(我也尝试了一个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),
    }
}
Run Code Online (Sandbox Code Playgroud)

编译导致的错误:

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) {
   |              ^^^^
Run Code Online (Sandbox Code Playgroud)

Erc*_*den 43

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

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,
}
Run Code Online (Sandbox Code Playgroud)

  • 你是绝对正确的@NicholasPipitone。我什至不完全确定两年多前的评论是什么意思。 (11认同)
  • @jhpratt我认为将“不安全”放入宏中会违背借用检查器的目的。如果您从两个不同的位置可变地触摸“db”,您将会出现段错误,可能最好将“unsafe”保留在那里,这样您就可以明确地说“如果我不借用检查自己,我可能会出现段错误”。除非你的宏名称中有“不安全”,在这种情况下继续。 (10认同)
  • 使用`static mut`选项,是否意味着使用连接的每一段代码都必须标记为不安全? (9认同)

Shn*_*sel 28

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

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

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
    });
}
Run Code Online (Sandbox Code Playgroud)

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

与常规对象不同static,thread-local!(static ...)您可以创建几乎任意的对象,包括那些需要用于初始化的堆分配的对象Vec,HashMap以及其他对象.

如果你不能立即初始化值,例如它取决于用户输入,你可能也必须扔进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
    });
}
Run Code Online (Sandbox Code Playgroud)


Abb*_*sal 16

看看conststatic锈书的部分.

您可以使用以下内容:

const N: i32 = 5; 
Run Code Online (Sandbox Code Playgroud)

要么

static N: i32 = 5;
Run Code Online (Sandbox Code Playgroud)

在全球空间.

但这些都不可变.对于可变性,您可以使用以下内容:

static mut N: i32 = 5;
Run Code Online (Sandbox Code Playgroud)

然后引用它们像:

unsafe {
    N += 1;

    println!("N: {}", N);
}
Run Code Online (Sandbox Code Playgroud)

  • 请解释一下 `const Var: Ty` 和 `static Var: Ty` 之间的区别? (4认同)
  • @sb27 实际上 `const` 使它只是一个编译器可以在任何需要的地方内联的值,并且实际上不会占用内存中的固定位置。(它可能没有被链接器放置在任何地方。)并且“static”不会使其可变。使它可变的是“mut”。至少,如果我理解正确的话……我学习 Rust 才几天。 (3认同)
  • @PeterHansen 你好,Rustacean 同学,我希望你喜欢这门语言 ^^,但你是绝对正确的,至少文档是这么说的(参见 https://doc.rust-lang.org/std/keyword.const.html)。 (2认同)

Yif*_*Sun 15

我是 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()));
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • `&amp;'static Arc&lt;Mutex&lt;...&gt;&gt;` 没有意义,因为它永远不会被销毁,也没有理由克隆它;你可以只使用 `&amp;'static Mutex&lt;...&gt;`。 (16认同)

Sak*_*ive 13

在此输入图像描述

我认为此页面很好地涵盖了大多数方面https://www.sitepoint.com/rust-global-variables/


at5*_*321 9

从 Rust 1.70 开始,我们还可以在只需要初始化(写入)一次的静态全局变量的情况下使用OnceLock同步原语。

这是静态全局只读的示例HashMap

use std::collections::HashMap;
use std::sync::OnceLock;

static GLOBAL_MAP: OnceLock<HashMap<String, i32>> = OnceLock::new();

fn main() {
    let m = get_hash_map_ref();
    assert_eq!(m.get("five"), Some(&5));
    assert_eq!(m.get("seven"), None);
    std::thread::spawn(|| {    
        let m = get_hash_map_ref();
        println!("From another thread: {:?}", m.get("five"));
    }).join().unwrap();
}

fn get_hash_map_ref() -> &'static HashMap<String, i32> {
    GLOBAL_MAP.get_or_init(|| {
        create_fixed_hash_map()
    })
}

fn create_fixed_hash_map() -> HashMap<String, i32> {
    let mut m = HashMap::new();
    m.insert("five".to_owned(), 5);
    m.insert("ten".to_owned(), 10);
    m
}
Run Code Online (Sandbox Code Playgroud)

可以看出,该映射可以从不同的线程访问。

请注意,它HashMap::new()不是const(至少现在还不是),这就是为什么我们(仍然)不能拥有像Rustconst MY_MAP: HashMap<...> = ...那样的东西。

  • 谢谢你。使用 Rust 编码已经超过 3 年了,我的解决方案需要大量工作并且已经过时了。这个新功能是一个不同的解决方案,但极大地提高了生活质量!:) 好消息是我在网上查了一下以确定。 (2认同)

San*_*Ben 6

如果您使用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");
}
Run Code Online (Sandbox Code Playgroud)

  • [现有答案已经讨论了惰性静态](/sf/answers/3806084411/)。请[编辑]您的答案,以清楚地展示该答案与现有答案相比带来的价值。 (2认同)