4

The UnsafeCell documentation says

The UnsafeCell<T> type is the only legal way to obtain aliasable data that is considered mutable.

The only construction method is:

pub const fn new(value: T) -> UnsafeCell<T>

However, it is not possible to create a c_void, we can only create *mut c_void or *const c_void.

Is it possible to create UnsafeCell<c_void> from a *mut c_void? With this, we can let the compiler know that the pointer can point to something mutable.

Or is this not necessary? Can we always use *mut c_void even we know some FFI call will mutate the data it points to and we have multiple references to it?

A use case would be:

struct FFIStruct { v: UnsafeCell<c_void>, other_fields: ... }
impl FFIStruct {
    // We don't want to require &mut self, as we 
    // are sure private call_ffi() will always be called 
    // sequentially, and we don't want to stop
    // status() being callable during the call
    fn call_ffi(&self){ ffi_function(self.v.get()) }
    pub fn status(&self) -> FFIStatus { ... }
}

Now how do we create FFIStruct? Or just use *mut c_void would be OK?

Example code to create &Cell<c_void>

Requires #![feature(as_cell)]:

unsafe fn get_cell<'a>(p: *mut c_void) -> &'a Cell<c_void> {
    Cell::from_mut(&mut *p)
}
15
  • 3
    Do you actually access v in your code, or is it opaque? If it's opaque, all of this is not necessary. Commented Oct 2, 2018 at 6:14
  • It is opaque and only the FFI implementation can touch it. But can you be more specific to say what is not necessary? Do you mean the use of UnsafeCell? or *mut c_void (which I think would be necessory...) Commented Oct 2, 2018 at 6:22
  • UnsafeCell informs the Rust compiler that a value may change at any time, even though the compiler can see a shared reference to it. This is important for when the compiler wants to use the value, so that it knows it can't just hold it in a register. But if the compiler never touches the field, because it's only touched by FFI functions, then the annotation isn't necessary. Commented Oct 2, 2018 at 6:26
  • of course you can't what is the size of void ? Commented Oct 2, 2018 at 6:49
  • @Stargateur UnsafeCell does not require Sized, so we can put DST in it... Commented Oct 2, 2018 at 6:50

2 Answers 2

4

TL;DR: Just use *mut Foo. Cells of any kind are not needed here.


Disclaimer: there is no formal Rust memory model, yet.

You cannot create this type, period, because you cannot1 create an instance of c_void.

The thing is, you don't need to create such a type. Aliasing is not spatial but temporal. You can have multiple *mut T pointing to the same place and it doesn't matter until you try to access one. This essentially converts it to a reference and the aliasing requirements need to be upheld while that reference is around.

raw pointers fall outside of Rust's safe memory model.

— The Rustonomicon

Different from references and smart pointers, raw pointers:

  • Are allowed to ignore the borrowing rules by having both immutable and mutable pointers or multiple mutable pointers to the same location
  • Aren’t guaranteed to point to valid memory
  • Are allowed to be null
  • Don’t implement any automatic cleanup

¸— The Rust Programming Language

See also:

1 You technically can, but that's only because of an implementation and backwards compatibility limitation.

Sign up to request clarification or add additional context in comments.

7 Comments

This is what I thought. However, UnsafeCell was refered to as "the root of interior mutability", and this example seems to imply that, *mut T is another root. Am I right? (Maybe I should open another question...)
@EarthEngine Interior mutability means you can mutate something while only holding a shared reference (&T) to it. Raw pointers don't let you do that. Mutating something through a *mut T isn't interior mutability, that's just regular mutability.
I know. But when you look at my FFIStruct example, I wanted to hide the fact that self.v can be mutated while only holding a shared reference &self to it. Is this interior mutability? Also, please check my example for &Cell<c_void>.
@EarthEngine self.v must not be mutated by FFI code because the pointer itself is accessed as an immutable reference (via &self). The value that self.v points to can be mutated because it's behind a raw pointer.
@EarthEngine Interior mutability is not a concern for you until you create a shared reference to some data. And I mean directly to that data, not transitively through a raw pointer. So I think every single mention of UnsafeCell and interior mutability in this discussion is a red herring. You never have a shared ref to begin with.
|
-1

After some internal discussion in the Rust forum and a discussion on RFC 1861, I realize c_void is just a common workaround and other options exists like struct Opaque<UnsafeCell<()>>.

So I concluded what I needed here is *const UnsafeCell<c_void>. From its type we know:

  • This is a raw pointer, so it is suitable to send to FFI immediately;
  • The raw pointer assumes const, means any casting to *mut T will UB and programmer should avoid it. This protects the memory it points to being modified within Rust (unless through UnsafeCell, of cause);
  • It contains UnsafeCell, so it implies interior mutability. This justifies the FFI function being able to mutate it;
  • It is not possible to create a c_void, so as for UnsafeCell<c_void>. They can only be created by FFI.

Demostrate:

use std::cell::UnsafeCell;
use std::os::raw::c_void;

//Let's say all those functions are in an FFI module,
//with the exact same behaviour
fn ffi_create() -> *mut c_void {
    Box::into_raw(Box::new(0u8)) as *mut c_void
}
unsafe fn ffi_write_u8(p: *mut c_void, v:u8) {
    *(p as *mut u8) = v;
}
unsafe fn ffi_read_u8(p: *mut c_void) -> u8 {
    *(p as *mut u8)
}

fn main() {
    unsafe {
        //let's ignore ffi_destroy() for now
        let pointer = ffi_create() as *const UnsafeCell<c_void>;
        let ref_pointer = &pointer;        
        ffi_write_u8((&*pointer).get(), 7);
        let integer = ffi_read_u8((&**ref_pointer).get());
        assert_eq!(integer, 7);
    }
}

It is interesting how easy and ergonomic (yet expressive) to convert between *mut c_void and *const UnsafeCell<c_void>.

1 Comment

I don't think this is the type you want. I am afraid your question was already somewhat ill-posed, which is why the answers also became confusing. The thing is, UnsafeCell is only relevant for shared references. If you do FFI and everything is behind a ptr indirection, then you do not have to worry about UnsafeCell. Just use *mut and wrap it in a newtype. The nomicon has some further information.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.