wake-up-neo.com

Ist es möglich, globale Variablen in Rust zu verwenden?

Ich weiß, dass generell globale Variablen zu vermeiden sind. Ich denke jedoch in einem praktischen Sinn, es ist manchmal wünschenswert (in Situationen, in denen die Variable Bestandteil des Programms ist), sie zu verwenden.

Um Rust zu lernen, schreibe ich derzeit ein Datenbank-Testprogramm mit sqlite3 und dem Rust/sqlite3-Paket auf GitHub. Daher ist es (in meinem Testprogramm) (als Alternative zu einer globalen Variablen) erforderlich, die Datenbankvariable zwischen Funktionen zu übergeben, von denen etwa ein Dutzend vorhanden ist. Ein Beispiel ist unten.

  1. Ist es möglich und möglich und wünschenswert, globale Variablen in Rust zu verwenden?

  2. Kann ich im folgenden Beispiel eine globale Variable deklarieren und verwenden?

extern crate sqlite;

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

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

Ich habe folgendes versucht, aber es scheint nicht ganz richtig zu sein und führte zu den folgenden Fehlern (ich habe es auch mit einem unsafe-Block versucht):

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

Fehler, die sich aus dem Kompilieren ergeben haben:

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) {
   |              ^^^^
58
Brian Oh

Es ist möglich, aber keine direkte Zuweisung von Heaps. Die Heap-Zuordnung wird zur Laufzeit ausgeführt. Hier einige Beispiele:

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,
}
30
Ercan Erden

Sie können statische Variablen relativ einfach verwenden, solange sie Thread-lokal sind.  

Der Nachteil ist, dass das Objekt für andere Threads, die Ihr Programm erzeugt, möglicherweise nicht sichtbar ist. Der Vorteil ist, dass er im Gegensatz zu einem wirklich globalen Zustand absolut sicher ist und keine Schmerzen verursacht - ein wahrer globaler Zustand ist in jeder Sprache ein enormer Schmerz. Hier ist ein Beispiel:

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

Hier erstellen wir eine Thread-lokale statische Variable und verwenden sie dann in einer Funktion. Beachten Sie, dass es statisch und unveränderlich ist. Dies bedeutet, dass die Adresse, an der sie sich befindet, unveränderlich ist, aber dank RefCell ist der Wert selbst veränderbar. 

Im Gegensatz zu regulären static können Sie in thread-local!(static ...) ziemlich beliebige Objekte erstellen, einschließlich solcher, die Heap-Zuweisungen für die Initialisierung erfordern, wie Vec, HashMap und andere.

Wenn Sie den Wert nicht sofort initialisieren können, z. es hängt von der Benutzereingabe ab. Möglicherweise müssen Sie auch Option dort einschleusen. In diesem Fall wird der Zugriff auf dieses Objekt etwas unhandlich: 

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

Schauen Sie sich den Abschnitt const und static des Rust-Buches an .

Sie können etwas wie folgt verwenden:

const N: i32 = 5; 

oder

static N: i32 = 5;

im globalen Raum.

Diese sind jedoch nicht veränderbar. Für die Veränderlichkeit können Sie Folgendes verwenden:

static mut N: i32 = 5;

Dann verweisen Sie sie wie:

unsafe {
    N += 1;

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

Ich weiß nicht, warum niemand über eine Lösung spricht, die Arc verwendet. Ich bin auch neu in Rust, aber diese Lösung scheint zu funktionieren.

#[marco_use]
extern crate lazy_static;

use std::sync::{Arc, Mutex};

lazy_static! {
    static ref GLOBAL: Arc<Mutex<GlobalType> =
        Arc::new(Mutex::new(GlobalType::new()));
}

Eine andere Lösung besteht darin, ein Tx/RX-Paar eines Querträgerkanals als unveränderliche globale Variable zu deklarieren. Der Kanal sollte begrenzt sein und nur ein Element enthalten. Wenn Sie die globale Variable initialisieren, verschieben Sie die globale Instanz in den Kanal. Wenn Sie die globale Variable verwenden, öffnen Sie den Kanal, um ihn abzurufen, und drücken Sie ihn zurück, wenn Sie ihn nicht mehr benötigen. 

Beide Lösungen sollten einen Rust-sicheren Ansatz für die Verwendung globaler Variablen bieten. 

0
Yifan Sun