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