Rustでグローバル変数を使用することは可能ですか?


104

一般的に、グローバル変数は避けるべきです。それでも、実用的な意味では、(変数がプログラムに不可欠な状況では)使用することが望ましい場合があると思います。

Rustを学ぶために、現在、GitHubでsqlite3とRust / sqlite3パッケージを使用したデータベーステストプログラムを作成しています。その結果、(私のテストプログラムでは)(グローバル変数の代替として)約12個ある関数間でデータベース変数を渡す必要があります。以下に例を示します。

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


ここで、OPで発生しているエラーはConnectionOption<Connection>型の内部に格納しようとしたり、をOption<Connection>として使用したりすることに関係していることに注意してくださいConnection。それらのエラーが(を使用してSome())解決され、unsafeブロックを使用した場合、最初に試みたように、それらのコードは機能します(ただし、スレッドに対して安全ではありません)。
TheHansinator

回答:


65

それは可能ですが、ヒープの割り当ては直接許可されていません。ヒープの割り当ては実行時に実行されます。以下にいくつかの例を示します。

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

13
static mutオプション、接続を使用するコードのすべての部分が安全ではないとしてマークされなければならないことを意味するのでしょうか?
Kamek

1
@Kamek最初のアクセスは安全でなければなりません。私は通常、それをマスクするためにマクロの薄いラッパーを使用します。
jhpratt

44

静的変数は、スレッドローカルである限り、かなり簡単に使用できます。

欠点は、プログラムが生成する他のスレッドからオブジェクトが見えないことです。良い点は、真にグローバルな状態とは異なり、それは完全に安全であり、使用するのに苦痛ではないということです。真のグローバルな状態は、どの言語でも大きな苦痛です。次に例を示します。

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

22

Rustブックのconstおよびstaticセクションをご覧ください。

次のように使用できます。

const N: i32 = 5; 

または

static N: i32 = 5;

グローバル空間で。

しかし、これらは変更可能ではありません。可変性のために、あなたは次のようなものを使うことができます:

static mut N: i32 = 5;

次に、それらを次のように参照します。

unsafe {
    N += 1;

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

1
const Var: Tyとの違いを説明してくださいstatic Var: Ty
Nawaz

4

私はルストは初めてですが、この解決策はうまくいくようです:

#[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つの要素しか保持できません。グローバル変数を初期化するときに、グローバルインスタンスをチャネルにプッシュします。グローバル変数を使用する場合は、チャネルをポップして取得し、使用が終わったら押し戻します。

どちらのソリューションも、グローバル変数を使用するための安全なアプローチを提供する必要があります。


10
ポイントはありません&'static Arc<Mutex<...>>、それは破壊することはできませんし、今までそれをクローン化する理由はありませんので。そのまま使用できます&'static Mutex<...>
trentcl

1

ドキュメントにあるように、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");
}

既存の答えはすでに怠惰な静的語ります。回答を編集して、この回答が既存の回答と比較してもたらす価値を明確に示してください。
シェプマスター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.