type globales crear global-variables rust

global-variables - globales - global post wordpress



¿Es posible usar variables globales en Rust? (3)

Sé que, en general, se deben evitar las variables globales. Sin embargo, creo que en un sentido práctico, a veces es deseable (en situaciones donde la variable es integral al programa) usarlos.

Para aprender Rust, actualmente estoy escribiendo un programa de prueba de base de datos usando sqlite3 y el paquete Rust / sqlite3 en GitHub. En consecuencia, eso requiere (en mi programa de prueba) (como alternativa a una variable global), pasar la variable de la base de datos entre funciones de las cuales hay alrededor de una docena. Un ejemplo es abajo.

  1. ¿Es posible, factible y deseable usar variables globales en Rust?

  2. Dado el ejemplo a continuación, ¿puedo declarar y usar una variable global?

extern crate sqlite; fn main() { let db: sqlite::Connection = open_database(); if !insert_data(&db, insert_max) { return; } }

Intenté lo siguiente, pero no parece ser del todo correcto y resultó en los siguientes errores (lo intenté también con un bloque 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), } }

Errores que resultaron de la compilación:

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


Es posible, pero no se permite la asignación de montones directamente. La asignación de montón se realiza en tiempo de ejecución. Aquí hay algunos ejemplos:

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


Mire la sección static y static del libro Rust .

Puedes usar algo de la siguiente manera:

const N: i32 = 5;

o

static N: i32 = 5;

en el espacio global.

Pero estos no son mutables. Para la mutabilidad, podrías usar algo como:

static mut N: i32 = 5;

Luego haz referencia a ellos como:

unsafe { N += 1; println!("N: {}", N); }


Puede usar variables estáticas bastante fácilmente siempre que sean locales de subprocesos.

La desventaja es que el objeto no estará visible para otros hilos que su programa pueda engendrar. Lo bueno es que, a diferencia del estado verdaderamente global, es totalmente seguro y no es doloroso de usar: el verdadero estado global es un dolor masivo en cualquier idioma. Aquí hay un ejemplo:

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

Aquí creamos una variable estática thread-local y luego la usamos en una función. Tenga en cuenta que es estático e inmutable; esto significa que la dirección en la que reside es inmutable, pero gracias a RefCell el valor en sí mismo será mutable.

A diferencia de la static regular, en thread-local!(static ...) puede crear objetos bastante arbitrarios, incluidos aquellos que requieren asignaciones de montón para la inicialización como Vec , HashMap y otros.

Si no puede inicializar el valor de inmediato, por ejemplo, depende de la entrada del usuario, también puede tener que lanzar la Option allí, en cuyo caso el acceso a él se vuelve un poco difícil de manejar:

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