Rust'ta global değişkenler kullanmak mümkün mü?


108

Genel olarak global değişkenlerden kaçınılması gerektiğini biliyorum. Yine de, pratik anlamda (değişkenin programa entegre olduğu durumlarda) bunları kullanmanın bazen istenebileceğini düşünüyorum.

Rust'u öğrenmek için şu anda GitHub'da sqlite3 ve Rust / sqlite3 paketini kullanarak bir veritabanı test programı yazıyorum. Sonuç olarak, bu (benim test programımda) (genel bir değişkene alternatif olarak) veritabanı değişkenini yaklaşık bir düzine var olan fonksiyonlar arasında geçirmeyi gerektirir. Bir örnek aşağıdadır.

  1. Rust'ta global değişkenleri kullanmak mümkün ve uygulanabilir ve arzu edilir mi?

  2. Aşağıdaki örnek göz önüne alındığında, bir global değişken bildirebilir ve kullanabilir miyim?

extern crate sqlite;

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

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

Aşağıdakini denedim, ancak tam olarak doğru görünmüyor ve aşağıdaki hatalarla sonuçlandı (bir unsafeblokla da denedim ):

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

Derlemeden kaynaklanan hatalar:

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

4
Bir İçin güvenli çözümü, lütfen bkz ben küresel, değişken singleton yaratabilirim? .
Shepmaster

Ben OP yaşadığını hatalar saklamak için çalışıyor ilgisi sahip olduklarının belirtilmesi gereken Connectionbir iç Option<Connection>türü ve bir kullanmaya çalışıyor Option<Connection>bir şekilde Connection. Bu hatalar çözülürse (kullanılarak Some()) ve unsafebaşlangıçta denedikleri gibi bir blok kullanırlarsa, kodları çalışacaktır (iş parçacığı güvensiz bir şekilde de olsa).
TheHansinator

Yanıtlar:


66

Mümkün, ancak yığın tahsisine doğrudan izin verilmez. Yığın tahsisi, çalışma zamanında gerçekleştirilir. İşte birkaç örnek:

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
ile static mutseçeneği, bu bağlantı kullanır kodun her parçası güvensiz olarak işaretlenmesi gerektiği anlamına geliyor?
Kamek

1
@Kamek İlk erişim güvenli olmamalıdır. Bunu maskelemek için genellikle ince bir makro sarıcı kullanırım.
jhpratt

48

Statik değişkenleri, evre-yerel oldukları sürece oldukça kolay bir şekilde kullanabilirsiniz.

Bunun dezavantajı, nesnenin programınızın oluşturabileceği diğer iş parçacıkları tarafından görünmeyeceğidir. Bunun tersi, gerçek küresel devletin aksine, tamamen güvenlidir ve kullanımı bir acı değildir - gerçek küresel durum her dilde büyük bir acıdır. İşte bir örnek:

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

Burada bir evre-yerel statik değişken oluşturuyoruz ve sonra onu bir fonksiyonda kullanıyoruz. Statik ve değişmez olduğunu unutmayın; bu, bulunduğu adresin değişmez olduğu anlamına gelir, ancak RefCelldeğer sayesinde kendisi değiştirilebilir olacaktır.

Normalden farklı olarak static, içinde , ve diğerleri thread-local!(static ...)gibi başlatma için yığın ayırmaları gerektirenler de dahil olmak üzere hemen hemen rastgele nesneler oluşturabilirsiniz .VecHashMap

Değeri hemen başlatamazsanız, örneğin kullanıcı girdisine bağlıysa, Optionoraya da atmanız gerekebilir , bu durumda ona erişmek biraz zahmetli olur:

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

23

Bak constve staticPas kitabın bölümünde .

Aşağıdaki gibi bir şey kullanabilirsiniz:

const N: i32 = 5; 

veya

static N: i32 = 5;

küresel alanda.

Ancak bunlar değiştirilebilir değildir. Değişkenlik için aşağıdaki gibi bir şey kullanabilirsiniz:

static mut N: i32 = 5;

Sonra onlara şu şekilde referans verin:

unsafe {
    N += 1;

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

1
Lütfen const Var: Tyve arasındaki farkı açıklayın static Var: Ty?
Nawaz

6

Rust'ta yeniyim, ancak bu çözüm işe yarıyor gibi görünüyor:

#[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()));
}

Diğer bir çözüm ise, bir çapraz ışın kanalı tx / rx çiftini değişmez bir global değişken olarak bildirmektir. Kanal sınırlandırılmalıdır ve yalnızca 1 öğe tutabilir. Global değişkeni başlattığınızda, global örneği kanala itin. Global değişkeni kullanırken, almak için kanalı açın ve onu kullanarak işiniz bittiğinde geri itin.

Her iki çözüm de global değişkenleri kullanmak için güvenli bir yaklaşım sağlamalıdır.


11
Hiçbir anlamı yok &'static Arc<Mutex<...>>çünkü asla yok edilemez ve onu klonlamak için hiçbir neden yok; sadece kullanabilirsiniz &'static Mutex<...>.
trentcl

1

Dokümanlarda görüldüğü gibi lazy_static makroyu kullanırsanız, statik değişkenler için yığın ayırmaları mümkündür

Bu makroyu kullanarak, kodun çalıştırılması için çalışma zamanında yürütülmesini gerektiren statiklere sahip olmak mümkündür. Bu, vektörler veya karma haritalar gibi yığın tahsisi gerektiren her şeyin yanı sıra hesaplanacak işlev çağrılarını gerektiren her şeyi içerir.

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

Bir varolan cevap zaten tembel statik bahsediyor . Mevcut cevaplara kıyasla bu cevabın ne gibi bir değer kattığını açıkça göstermek için lütfen cevabınızı düzenleyin .
Shepmaster
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.