Having the mut
keyword in a static is never the right thing to do (curiously enough, someone else recently tried a similar-ish pattern, so I am thinking about making a PR on lazy_static!
to error on some of these cases).
A &mut _
reference in Rust is not (directly) a mutable reference (to "an object") but a unique reference to it. And once something is stored in a static
, it can be accessed from anywhere. It is thus potentially aliased, and cannot yield &mut _
references without a runtime check. In Rust's standard library, this runtime check is performed by Interior Mutability (that also needs to be Sync
to be in a static
): Mutex<_>
or RwLock<_>
.
This way, you can get (something that DerefMut
s to) a &mut T
from static ref SOME_GLOBAL: Mutex<T>
by doing &mut SOME_GLOBAL.lock().unwrap()
.
You can read more about this "classic" pattern in this other post of mine.
However, your case is a little more complicated, since you are dealing with raw pointers from FFI (the first one you get is an owning pointer, such as the one you use when calling cassandra_free
, the other ones are borrows of the pointee; a usual pattern in C. It is hard to spot the difference, since both are *mut CassSession
).
Thus, before anything else (e.g., storing the *mut CassSession
or something wrapping it in a static
), you need to have safe Rust wrappers around these raw pointers, expressing the right ownership / borrowing semantics.
Example
/// The *-sys Rust crate exposing FFI functions
mod lib_sys {
use ::libc::c_int;
#[repr(C)]
pub
struct Foo {
_opaque: [u8; 0],
}
extern "C" {
pub fn new_foo () -> *mut Foo;
pub fn use_foo (_: *mut Foo, args: c_int);
pub fn free_foo (_: *mut Foo);
}
}
/* == Time for the safe wrappers ==
* 1. Distinguish the different *mut Foo usages (ownership or borrowing?)
* - new_foo() creates an owning pointer
* - use_foo() just borrows the pointee
* - free_foo() is the destructor
*
* 2. Create a wrapper over *mut Foo, and express the detected semantics with that wrapper:
* - new_foo() should be used to create an (owned) instance of the wrapper,
* - use_foo() should just borrow (&mut _) from the wrapper,
* - free_foo() should be called on Drop::drop (and only then):
*/
use ::std::{*,
convert::TryInto,
};
use lib_sys as ffi;
pub
struct Foo {
// instead of *mut ffi::Foo, let's use NonNull for more safety
ffi: ptr::NonNull<ffi::Foo>,
}
impl Foo {
pub
fn new () -> Self
{
unsafe {
Self {
ffi: ptr::NonNull::new(ffi::new_foo()).unwrap(),
}
}
}
pub
fn do_use (&mut self, args: i32)
{
unsafe {
ffi::use_foo(
self.ffi.as_ptr(),
args.try_into().unwrap(), // checked conversion from i32 to c_int
)
}
}
}
impl Drop for Foo {
fn drop (&mut self)
{
unsafe {
ffi::free_foo(self.ffi.as_ptr())
}
}
}
Usage
fn main ()
{
let mut foo = Foo::new(); // new_foo()
foo.do_use(42); // use_foo()
// mem::drop(foo); // free_foo()
}
Then, with such a safe wrapper of *mut CassSession
, you could have
lazy_static! {
static ref SESSION: Mutex<CassSession> = Mutex::new(
CassSession::new() // calls cass_session_new() and checks its success
);
}