Wiem, że generalnie należy unikać zmiennych globalnych. Niemniej jednak uważam, że w sensie praktycznym czasami jest pożądane (w sytuacjach, gdy zmienna jest integralna z programem), aby ich używać.
Aby nauczyć się Rusta, obecnie piszę program do testowania bazy danych przy użyciu sqlite3 i pakietu Rust / sqlite3 na GitHub. W konsekwencji wymaga to (w moim programie testowym) (jako alternatywa dla zmiennej globalnej), aby przekazać zmienną bazy danych pomiędzy funkcjami, których jest ich kilkanaście. Przykład poniżej.
Czy jest możliwe, wykonalne i pożądane używanie zmiennych globalnych w Rust?
Biorąc pod uwagę poniższy przykład, czy mogę zadeklarować i użyć zmiennej globalnej?
extern crate sqlite;
fn main() {
let db: sqlite::Connection = open_database();
if !insert_data(&db, insert_max) {
return;
}
}
Próbowałem wykonać następujące czynności, ale nie wydaje się to być całkiem poprawne i spowodowało poniższe błędy (próbowałem również z unsafe
blokiem):
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),
}
}
Błędy wynikające z kompilacji:
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) {
| ^^^^
źródło
Connection
wewnątrzOption<Connection>
typu i próbą użyciaOption<Connection>
jakoConnection
. Jeśli te błędy zostały rozwiązane (przy użyciuSome()
) i użyliunsafe
bloku, tak jak pierwotnie próbowali, ich kod działałby (choć w sposób niebezpieczny dla wątków).Odpowiedzi:
Jest to możliwe, ale bezpośrednie przydzielanie sterty nie jest dozwolone. Alokacja sterty jest wykonywana w czasie wykonywania. Oto kilka przykładów:
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, }
źródło
static mut
opcją, czy oznacza to, że każdy fragment kodu korzystający z połączenia musi być oznaczony jako niebezpieczny?Zmiennych statycznych można używać dość łatwo, o ile są one lokalne dla wątku.
Wadą jest to, że obiekt nie będzie widoczny dla innych wątków, które może pojawić się w programie. Plusem jest to, że w przeciwieństwie do prawdziwie globalnego stanu jest całkowicie bezpieczny i nie jest uciążliwy w użyciu - prawdziwy globalny stan jest ogromnym problemem w każdym języku. Oto przykład:
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 }); }
Tutaj tworzymy zmienną statyczną lokalną dla wątku, a następnie używamy jej w funkcji. Zauważ, że jest statyczny i niezmienny; oznacza to, że adres pod którym się znajduje jest niezmienny, ale dzięki
RefCell
samej wartości będzie zmienny.W przeciwieństwie do zwykłego
static
, w programiethread-local!(static ...)
można tworzyć prawie dowolne obiekty, w tym te, które wymagają alokacji sterty do inicjalizacji, takich jakVec
,HashMap
i inne.Jeśli nie możesz zainicjować wartości od razu, np. Zależy to od danych wejściowych użytkownika, być może będziesz musiał
Option
tam również wrzucić , w takim przypadku dostęp do niej staje się nieco nieporęczny: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 }); }
źródło
Spójrz na sekcję
const
istatic
sekcję książki Rust .Możesz użyć czegoś w następujący sposób:
const N: i32 = 5;
lub
static N: i32 = 5;
w przestrzeni globalnej.
Ale te nie są zmienne. W przypadku zmienności możesz użyć czegoś takiego:
static mut N: i32 = 5;
Następnie odwołaj się do nich w ten sposób:
unsafe { N += 1; println!("N: {}", N); }
źródło
const Var: Ty
istatic Var: Ty
?Jestem nowy w Rust, ale to rozwiązanie wydaje się działać:
#[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())); }
Innym rozwiązaniem jest zadeklarowanie pary tx / rx kanału belki poprzecznej jako niezmiennej zmiennej globalnej. Kanał powinien być ograniczony i może zawierać tylko 1 element. Kiedy inicjalizujesz zmienną globalną, wypchnij instancję globalną do kanału. Korzystając ze zmiennej globalnej, przesuń kanał, aby go pobrać, i odepchnij go z powrotem po zakończeniu używania.
Oba rozwiązania powinny zapewniać bezpieczne podejście do wykorzystywania zmiennych globalnych.
źródło
&'static Arc<Mutex<...>>
ponieważ nigdy nie można go zniszczyć i nie ma powodu, aby go kiedykolwiek klonować; możesz po prostu użyć&'static Mutex<...>
.Alokacje sterty są możliwe dla zmiennych statycznych, jeśli używasz makra lazy_static, jak widać w dokumentacji
// 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"); }
źródło