2013-10-26 9 views
40

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

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

  1. Возможно ли и целесообразно использовать глобальные переменные в ржавчине?

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

для ** ** безопасное решение, пожалуйста, см [Как создать глобальный , mutable singleton?] (http://stackoverflow.com/q/27791532/155423). – Shepmaster

ответ

20

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

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

с опцией 'static mut', означает ли это, что каждый фрагмент кода, который использует соединение, должен быть помечен как небезопасный? – Kamek

10

Посмотрите на const and static section of the Rust book.

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

const N: i32 = 5; 

или

static N: i32 = 5; 

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

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

static mut N: i32 = 5; 

ссылаться на них нравится:

unsafe { 
    N += 1; 

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

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

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

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 
    }); 
} 
Смежные вопросы