While I continue to be skeptical about Rust as a general purpose programming language (my skepticism does not extend to its use as a systems programming language, as it is billed by the project), as I've said in other posts and therefore won't elaborate here, I continue to be intrigued by it, mostly because there are clearly smart people involved in this project, and so I continue to experiment with it in spare time.
I have written a personal financial management system for myself. The main application is written in C, using sqlite3 and gtk3. Let's just look at one issue that was easy to do in C and confounds me with Rust. Gtk callbacks frequently need to access the database. In C, I use statics inside the callback functions, initialized to NULL, to hold prepared queries. The functions test the static for NULL and if true, they prepare the query and store it in the static. If not, they just use it. So these statics allow me to do just-in-time preparation of only the prepared queries actually used, and then cache/memoize them for subsequent use.
In Rust, I've sketched out a bit of code like this and have tried to use lazy_static to (possibly) hold the prepared queries. This application is single-threaded and will remain so. I am using the sqlite crate. Its Statements (the result of prepare) do not have the Send trait and Statements must be stored so they are mutable. Apparently Rust insists on even non-mutable statics being thread-safe even in a single threaded application with no way to turn this off. So I can't define the type of a static as Statement, or Option and maybe use RefCell for interior mutability, to avoid declaring the static mutable. So even though I don't need a Mutex, I wrap the statement in a Mutex, in an effort to make the compiler happy. And then tried wrapping that in Arc, as The Book suggests. It still complains:
error[E0277]: the trait bound *mut sqlite3_sys::types::sqlite3_stmt: std::marker::Send
is not satisfied in sqlite::Statement<'static>
--> src/book.rs:29:9
|
29 | / lazy_static! {
30 | | static ref stmt:Option<Arc<Mutex<Statement<'static>>>> = None;
31 | | }
| |_________^ *mut sqlite3_sys::types::sqlite3_stmt
cannot be sent between threads safely
|
= help: within sqlite::Statement<'static>
, the trait std::marker::Send
is not implemented for *mut sqlite3_sys::types::sqlite3_stmt
= note: required because it appears within the type (*mut sqlite3_sys::types::sqlite3_stmt, *mut sqlite3_sys::types::sqlite3)
= note: required because it appears within the type sqlite::Statement<'static>
= note: required because of the requirements on the impl of std::marker::Send
for std::sync::Mutex<sqlite::Statement<'static>>
= note: required because of the requirements on the impl of std::marker::Sync
for std::sync::Arc<std::sync::Mutex<sqlite::Statement<'static>>>
= note: required because it appears within the type std::option::Option<std::sync::Arc<std::sync::Mutex<sqlite::Statement<'static>>>>
= note: required by lazy_static::lazy::Lazy
= note: this error originates in a macro outside of the current crate
So it appears that wrapping an object that doesn't implement Send in a Mutex (which claims to implement Send) in a single-threaded application just isn't good enough. Perhaps I'm just going about this the wrong way. Can someone please educate me on how to do this simple-minded task correctly in Rust?
Thanks.