Cannot borrow data in a `&` reference as mutable?

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