簡體   English   中英

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

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

我知道通常要避免使用全局變量。 盡管如此,我認為在實際意義上,有時需要使用它們(在變量是程序不可或缺的情況下)。

為了學習Rust,我目前正在使用sqlite3和GitHub上的Rust/sqlite3 package編寫一個數據庫測試程序。 因此,這需要(在我的測試程序中)(作為全局變量的替代方案)在大約有十幾個函數之間傳遞數據庫變量。 下面是一個例子。

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

這是可能的,但不允許直接進行堆分配。 堆分配在運行時執行。 這里有一些例子:

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

只要靜態變量是線程本地的,就可以相當容易地使用它們。

缺點是該對象對您的程序可能產生的其他線程不可見。 好處是,與真正的全局狀態不同,它是完全安全的,使用起來並不痛苦——真正的全局狀態在任何語言中都是一個巨大的痛苦。 這是一個例子:

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 book 的conststatic部分

您可以使用以下內容:

const N: i32 = 5;

或者

static N: i32 = 5;

在全球空間。

但這些不是可變的。 對於可變性,您可以使用以下內容:

static mut N: i32 = 5;

然后像這樣引用它們:

unsafe {
    N += 1;

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

我是 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()));
}

另一種解決方案是將交叉波束通道發送/接收對聲明為不可變的全局變量。 通道應該是有界的,並且只能容納一個元素。 初始化全局變量時,將全局實例推送到通道中。 使用全局變量時,彈出通道獲取它,使用完后將其推回。

這兩種解決方案都應該提供一種使用全局變量的安全方法。

如果您使用文檔中看到的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");
}

每晚使用 crate once_celllazy_staticSyncOnceCell

在此處輸入圖像描述

我認為這個頁面很好地涵蓋了大多數方面https://www.sitepoint.com/rust-global-variables/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM