Saya tahu bahwa secara umum, global-variabel yang harus dihindari. Namun demikian, saya pikir dalam arti praktis, hal ini kadang-kadang diinginkan (dalam situasi di mana variabel merupakan bagian integral dari program) untuk menggunakannya.
Dalam rangka untuk belajar Karat, I'm sedang menulis sebuah basis data menguji program dengan menggunakan sqlite3 dan Karat/sqlite3 paket di GitHub. Akibatnya, yang mengharuskan (di tes saya-program) (sebagai alternatif untuk variabel global), untuk lulus database variabel antara fungsi-fungsi yang ada sekitar selusin. Contoh di bawah ini.
Adalah hal yang mungkin dan layak dan diinginkan untuk menggunakan variabel global dalam Karat?
Diberikan contoh di bawah ini, saya bisa menyatakan dan menggunakan variabel global?
extern crate sqlite;
fn main() {
let db: sqlite::Connection = open_database();
if !insert_data(&db, insert_max) {
return;
}
}
Aku mencoba mengikuti, tapi itu doesn't tampaknya cukup tepat dan mengakibatkan kesalahan-kesalahan di bawah ini (saya mencoba juga dengan sebuah tidak aman
block):
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),
}
}
Kesalahan yang dihasilkan dari kompilasi:
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) {
| ^^^^
It's mungkin tapi tidak ada tumpukan alokasi diperbolehkan langsung. Alokasi timbunan dilakukan pada saat runtime. Berikut ini adalah beberapa contoh:
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,
}
Anda dapat menggunakan variabel statis cukup mudah asalkan mereka thread-lokal.
The downside adalah bahwa objek tidak akan terlihat oleh thread lain program anda mungkin bertelur. Keuntungannya adalah bahwa tidak seperti benar-benar global negara, hal ini sepenuhnya aman dan tidak sakit untuk menggunakan - benar global negara besar yang sakit dalam bahasa apapun. Berikut ini's contoh:
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
});
}
Di sini kita membuat sebuah thread-lokal variabel statis dan kemudian menggunakannya dalam fungsi. Perhatikan bahwa itu adalah statis dan tidak berubah; ini berarti bahwa alamat di mana ia berada adalah kekal, tapi berkat RefCell
nilai itu sendiri akan bisa berubah.
Tidak seperti biasa statis
, dalam thread-lokal!(statis ...)
anda dapat membuat cukup banyak objek sewenang-wenang, termasuk orang-orang yang membutuhkan tumpukan alokasi untuk inisialisasi seperti Vec
, HashMap
dan lain-lain.
Jika anda tidak dapat menginisialisasi nilai langsung, misalnya, itu tergantung pada input pengguna, anda mungkin juga harus membuang Pilihan
di sana, dalam hal mengakses itu mendapat sedikit berat:
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
});
}
Lihat const
dan statis
bagian dari Karat pesan.
Anda dapat menggunakan sesuatu seperti berikut:
const N: i32 = 5;
atau
static N: i32 = 5;
di ruang global.
Tapi ini tidak bisa berubah. Untuk berubah-ubah, anda bisa menggunakan sesuatu seperti:
static mut N: i32 = 5;
Kemudian referensi mereka seperti:
unsafe {
N += 1;
println!("N: {}", N);
}
Saya tidak tahu mengapa tidak ada yang berbicara tentang solusi yang menggunakan Arc
. Saya juga baru Karat, tapi ini sepertinya solusi ini bekerja.
``karat
extern peti lazy_static;
menggunakan std::sync::{Arc, Mutex};
lazy_static! {
statis ref GLOBAL: Arc<Mutex
Juga, solusi lain adalah untuk menyatakan palang saluran tx/rx pasangan abadi variabel global. Saluran harus dibatasi dan hanya bisa menampung 1 elemen. Ketika anda menginisialisasi variabel global, push contoh global ke dalam saluran. Bila menggunakan variabel global, pop channel untuk memperoleh itu dan mendorongnya kembali setelah selesai menggunakannya.
Kedua solusi harus memberikan Karat aman pendekatan untuk menggunakan variabel global.