简体   繁体   English

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

[英]Is it possible to use global variables in Rust?

I know that in general, global-variables are to be avoided.我知道通常要避免使用全局变量。 Nevertheless, I think in a practical sense, it is sometimes desirable (in situations where the variable is integral to the program) to use them.尽管如此,我认为在实际意义上,有时需要使用它们(在变量是程序不可或缺的情况下)。

In order to learn Rust, I'm currently writing a database test program using sqlite3 and the Rust/sqlite3 package on GitHub.为了学习Rust,我目前正在使用sqlite3和GitHub上的Rust/sqlite3 package编写一个数据库测试程序。 Consequently, that necessitates (in my test-program) (as an alternative to a global variable), to pass the database variable between functions of which there are about a dozen.因此,这需要(在我的测试程序中)(作为全局变量的替代方案)在大约有十几个函数之间传递数据库变量。 An example is below.下面是一个例子。

  1. Is it possible and feasible and desirable to use global variables in Rust?在 Rust 中使用全局变量是否可能、可行和可取?

  2. Given the example below, can I declare and use a global variable?鉴于下面的示例,我可以声明和使用全局变量吗?

extern crate sqlite;

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

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

I tried the following, but it doesn't appear to be quite right and resulted in the errors below (I tried also with an unsafe block):我尝试了以下方法,但它似乎不太正确并导致以下错误(我也尝试了一个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),
    }
}

Errors that resulted from compile:编译导致的错误:

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

It's possible, but heap allocation is not allowed directly.这是可能的,但不允许直接进行堆分配。 Heap allocation is performed at runtime.堆分配在运行时执行。 Here are a few examples:这里有一些例子:

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

You can use static variables fairly easily as long as they are thread-local.只要静态变量是线程本地的,就可以相当容易地使用它们。

The downside is that the object will not be visible to other threads your program might spawn.缺点是该对象对您的程序可能产生的其他线程不可见。 The upside is that unlike truly global state, it is entirely safe and is not a pain to use - true global state is a massive pain in any language.好处是,与真正的全局状态不同,它是完全安全的,使用起来并不痛苦——真正的全局状态在任何语言中都是一个巨大的痛苦。 Here's an example:这是一个例子:

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

Here we create a thread-local static variable and then use it in a function.这里我们创建一个线程局部静态变量,然后在函数中使用它。 Note that it is static and immutable;请注意,它是静态且不可变的; this means that the address at which it resides is immutable, but thanks to RefCell the value itself will be mutable.这意味着它所在的地址是不可变的,但是多亏了RefCell ,值本身将是可变的。

Unlike regular static , in thread-local!(static ...) you can create pretty much arbitrary objects, including those that require heap allocations for initialization such as Vec , HashMap and others.与常规的static不同,在thread-local!(static ...)中,您可以创建几乎任意的对象,包括那些需要堆分配以进行初始化的对象,例如VecHashMap等。

If you cannot initialize the value right away, eg it depends on user input, you may also have to throw Option in there, in which case accessing it gets a bit unwieldy:如果您不能立即初始化该值,例如它取决于用户输入,您可能还必须将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
    });
}

Look at the const and static section of the Rust book .查看Rust book 的conststatic部分

You can use something like the following:您可以使用以下内容:

const N: i32 = 5;

or或者

static N: i32 = 5;

in the global space.在全球空间。

But these are not mutable.但这些不是可变的。 For mutability, you could use something like:对于可变性,您可以使用以下内容:

static mut N: i32 = 5;

Then reference them like:然后像这样引用它们:

unsafe {
    N += 1;

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

I am new to Rust, but this solution seems to work:我是 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()));
}

Another solution is to declare a crossbeam channel transmit/receive pair as an immutable global variable.另一种解决方案是将交叉波束通道发送/接收对声明为不可变的全局变量。 The channel should be bounded and can only hold one element.通道应该是有界的,并且只能容纳一个元素。 When you initialize the global variable, push the global instance into the channel.初始化全局变量时,将全局实例推送到通道中。 When using the global variable, pop the channel to acquire it and push it back when done using it.使用全局变量时,弹出通道获取它,使用完后将其推回。

Both solutions should provide a safe approach to using global variables.这两种解决方案都应该提供一种使用全局变量的安全方法。

Heap allocations are possible for static variables if you use the lazy_static macro as seen in the documentation :如果您使用文档中看到的lazy_static宏,则静态变量可以进行堆分配:

Using this macro, it is possible to have statics that require code to be executed at runtime in order to be initialized.使用此宏,可以有需要在运行时执行代码才能初始化的静态变量。 This includes anything requiring heap allocations, like vectors or hash maps, as well as anything that requires function calls to be computed.这包括任何需要堆分配的东西,比如向量或哈希映射,以及任何需要计算函数调用的东西。

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

每晚使用 crate once_celllazy_staticSyncOnceCell

在此处输入图像描述

I think this page covers most aspects well https://www.sitepoint.com/rust-global-variables/我认为这个页面很好地涵盖了大多数方面https://www.sitepoint.com/rust-global-variables/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM