Подтвердить что ты не робот

Можно ли использовать глобальные переменные в Rust?

Я знаю, что в общем случае глобальных переменных следует избегать. Тем не менее, я думаю, что в практическом смысле иногда желательно (в ситуациях, когда переменная является неотъемлемой частью программы) использовать их.

Чтобы узнать Rust, я сейчас пишу программу тестирования базы данных, используя sqlite3 и пакет Rust/sqlite3 на GitHub. Следовательно, это требует (в моей тестовой программе) (как альтернативы глобальной переменной) передавать переменную базы данных между функциями, из которых около дюжины. Ниже приведен пример.

  • Возможно ли, возможно и желательно использовать глобальные переменные в Rust?

  • В приведенном ниже примере можно объявить и использовать глобальную переменную?

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

Ответ 1

Это возможно, но выделение кучи не допускается напрямую. Распределение кучи выполняется во время выполнения. Вот несколько примеров:

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

Ответ 2

Вы можете использовать статические переменные довольно легко, если они являются локальными потоками.

Недостатком является то, что объект не будет отображаться в других потоках, которые ваша программа может появиться. Потенциал роста заключается в том, что, в отличие от действительно глобального государства, он абсолютно безопасен и не является больным - истинное глобальное состояние - это огромная боль на любом языке. Вот пример:

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 ...) вы можете создавать довольно много произвольных объектов, включая те, которые требуют выделения кучи для инициализации, такие как Vec, HashMap и другие.

Если вы не можете сразу инициализировать значение, например, это зависит от ввода пользователя, вам также может понадобиться бросить 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
    });
}

Ответ 3

Посмотрите раздел const и static в книге Rust.

Вы можете использовать что-то следующим образом:

const N: i32 = 5; 

или

static N: i32 = 5;

в глобальном пространстве.

Но они не изменяемы. Для изменчивости вы можете использовать что-то вроде:

static mut N: i32 = 5;

Затем укажите их как:

unsafe {
    N += 1;

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

Ответ 4

Я не знаю, почему никто не говорит о решении, которое использует Arc. Я также новичок в 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()));
}

Кроме того, другое решение состоит в том, чтобы объявить пару tx/rx межканального канала в качестве неизменной глобальной переменной. Канал должен быть ограничен и может содержать только 1 элемент. Когда вы инициализируете глобальную переменную, вставьте глобальный экземпляр в канал. При использовании глобальной переменной извлеките канал, чтобы получить его, и отодвиньте его, когда закончите, используя его.

Оба решения должны обеспечивать безопасный подход Rust для использования глобальных переменных.

Ответ 5

Объявите переменную выше основной. Тогда это будет считаться глобальной переменной.