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)
}
Earth Engine
  • 10,048
  • 5
  • 48
  • 78
  • 3
    Do you actually access `v` in your code, or is it opaque? If it's opaque, all of this is not necessary. – Sebastian Redl Oct 02 '18 at 06: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...) – Earth Engine Oct 02 '18 at 06: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. – Sebastian Redl Oct 02 '18 at 06:26
  • of course you can't what is the size of void ? – Stargateur Oct 02 '18 at 06:49
  • @Stargateur `UnsafeCell` does not require `Sized`, so we can put DST in it... – Earth Engine Oct 02 '18 at 06:50
  • I'm not very aware of these concept in Rust, but I don't think it's possible to create a thing that hold void, whatever Rust magic feature have. That the entire point of void in C. You can't write `void a;`, you can only have `void *a;`. And this make sense because void is... nothing. You can't do anything to `*a` because it's a void type. Someone have to know what is its real type to use it. So I don't know how Rust would be capable of this. – Stargateur Oct 02 '18 at 06:57
  • A dynamically-sized type (DST) is not the same as an *unsized* / *zero-sized* type. – Shepmaster Oct 02 '18 at 13:21
  • `void` is not allowed as a value in C, so why do you expect to have one in Rust? – Peter Hall Oct 02 '18 at 16:15
  • I think you want an `UnsafeCell<*mut c_void>`. A mutable pointer to data of an unknown type, that may be mutated by other code during the existence of the pointer. – Phoenix Oct 02 '18 at 19:30
  • @Shepmaster If `c_void` is fully uninhabited it is like `UnsafeCell<!>`, but in reality although `c_void` is uninhabited but `*mut c_void` is not: it will point to an opaque value. What I expecting is to express the fact that this opaque value can be modified/changed even we have a shared reference to the object owns it. – Earth Engine Oct 02 '18 at 23:13
  • @Phoenix The semantic is quite different though. `UnsafeCell<*mut c_void>` contains additional abstraction: it is a pointer to a pointer. This also means the `get` method is not ready to be sent to FFI without `unsafe` deference. – Earth Engine Oct 02 '18 at 23:15
  • *a pointer to a pointer* — why do you believe that? I think that may be the root of your issue. – Shepmaster Oct 02 '18 at 23:15
  • @Shepmaster because, we can only get `*mut T` from `UnsafeCell`, which makes it work like a pointer. And the fact it supports DST also implies it is an indirection (means pointer, maybe I was wrong here?). So if we use a pointer as the contained value it will be a pointer to a pointer. – Earth Engine Oct 02 '18 at 23:19
  • @EarthEngine it would help if you could edit the question to be about the actual, high-level problem you are trying to solve. What happened here is that you want part-way down the *wrong* rabbit-hole, got stuck, and are asking a very specific question that is already making some wrong assumptions. So I suggest you take a couple steps back and focus on what you want to achieve: Which kind of API do you want to wrap, which kind of operations does it provide, what do the user-visible types look like in C, what would you like the user-visible types to look like in Rust? – Ralf Jung Oct 05 '18 at 16:38
  • But to literally answer your question, remember that `UnsafeCell` is NOT a pointer, so it seems what you want is a `*mut UnsafeCell`, which you can obtain from `*mut c_void` by a simple cast. That's even a safe operation. It's not at all what you want though. – Ralf Jung Oct 05 '18 at 16:40

2 Answers2

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 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...) – Earth Engine Oct 02 '18 at 23:48
  • 1
    @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. – trent Oct 03 '18 at 01:10
  • 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`. – Earth Engine Oct 03 '18 at 01:51
  • 1
    @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. – Shepmaster Oct 03 '18 at 02:03
  • @Shepmaster Just a simple question: Do `FFIStruct` has interior mutability? How many level behind the pointers does not matter: they are all owned by the struct, conceptually. – Earth Engine Oct 03 '18 at 02:17
  • @EarthEngine I honestly cannot answer that question — I have no idea ^_^ – Shepmaster Oct 04 '18 at 16:39
  • 2
    @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. – Ralf Jung Oct 05 '18 at 16:36
-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>.

Earth Engine
  • 10,048
  • 5
  • 48
  • 78
  • 2
    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](https://doc.rust-lang.org/nightly/nomicon/ffi.html#representing-opaque-structs). – Ralf Jung Oct 05 '18 at 16:33