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.
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:
Wrapper
is never moved.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()) }
}
}