1
votes

Let's consider a Rust wrapper library around a C library. This C library defines a struct and uses it as a mutable pointer throughout it's API.

The Rust wrapper defines the following struct with ptr pointing at data.

struct Wrapper {
    data: struct_from_c_t,
    ptr: *mut struct_from_c_t,
}

If all uses of this pointer are made within the Wrapper struct's lifetime, what other potential issues can I run into when using this pointer in unsafe code ?

Is dereference and use of this pointer always safe in this construct?

For detailed context, the goal is to be able to call FFI functions using this pointer from functions borrowing Wrapper non-mutably.

1
As far as I know what you are doing is undefined behavoir. You have to use an Unsafecell to achieve it. But I'm not very experienced with FFIs so this is not an answer ;)hellow
Thanks, not taking this as an answer but I will definitely take a look at UnsafeCell to see if I can use it to make a safe wrapper.Sisyphe
If you want to know anything about FFI, read the rustonomiconhellow
I see no reason to go to UnsafeCell. That's used for building your own abstractions, but existing abstractions are sufficient.Shepmaster

1 Answers

3
votes

This is generally a bad idea and it can go wrong very easily.

First, go read Why can't I store a value and a reference to that value in the same struct? for an in-depth explanation about why safe Rust prevents this construct at compile time.

TL;DR, if you ever move the Wrapper struct, the pointer will be invalid. Dereferencing it will cause undefined behavior (a bad thing).

If you can ensure that either of:

  1. The Wrapper is never moved.
  2. The ptr is updated every time you move the struct.

Then the pointer will be valid and safe to dereference (assuming all the other caveats about unsafe code are upheld).


What's worse is that there's no reason to keep the pointer in the first place; you can take a reference to a value and convert it into a pointer whenever you need:

extern "C" {
    fn ffi_fn(data: *mut struct_from_c_t);
}

struct Wrapper {
    data: struct_from_c_t,
}

impl Wrapper {
    fn do_thing(&mut self) {
        unsafe { ffi_fn(&mut self.data) }
    }
}

from functions borrowing Wrapper non-mutably

Without context, this seems like a dubious decision, but Rust has tools for interior mutability:

use std::cell::RefCell;

struct Wrapper {
    data: RefCell<struct_from_c_t>,
}

impl Wrapper {
    fn do_thing(&self) {
        unsafe { ffi_fn(&mut *self.data.borrow_mut()) }
    }
}