[global-variables] Rust에서 전역 변수를 사용할 수 있습니까?

일반적으로 전역 변수는 피해야한다는 것을 알고 있습니다. 그럼에도 불구하고 실용적인 의미에서 때때로 변수를 사용하는 것이 바람직하다고 생각합니다 (변수가 프로그램에 필수적인 상황에서).

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



답변

가능하지만 힙 할당은 직접 허용되지 않습니다. 힙 할당은 런타임에 수행됩니다. 다음은 몇 가지 예입니다.

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


답변

스레드 로컬 인 경우 정적 변수를 상당히 쉽게 사용할 수 있습니다.

단점은 프로그램이 생성 할 수있는 다른 스레드에 개체가 표시되지 않는다는 것입니다. 장점은 진정한 글로벌 상태와 달리 완전히 안전하고 사용하기 불편하지 않다는 것입니다. 진정한 글로벌 상태는 모든 언어에서 엄청난 고통입니다. 예를 들면 다음과 같습니다.

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


답변

Rust 책constand static부분을 보세요 .

다음과 같이 사용할 수 있습니다.

const N: i32 = 5; 

또는

static N: i32 = 5;

글로벌 공간에서.

그러나 이것들은 변경할 수 없습니다. 가변성을 위해 다음과 같이 사용할 수 있습니다.

static mut N: i32 = 5;

그런 다음 다음과 같이 참조하십시오.

unsafe {
    N += 1;

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


답변

저는 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 개의 요소 만 보유 할 수 있습니다. 전역 변수를 초기화 할 때 전역 인스턴스를 채널로 푸시합니다. 전역 변수를 사용하는 경우 채널을 팝하여 획득하고 사용이 끝나면 뒤로 밀어냅니다.

두 솔루션 모두 전역 변수 사용에 대한 안전한 접근 방식을 제공해야합니다.


답변

문서 에서 볼 수 있듯이 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");
}


답변