I’m doing some Rust FFI work for the Erlang NIF API, and I have these:
ErlNifEnv *enif_alloc_env();
void enif_free_env(ErlNifEnv* env);
This ErlNifEnv pointer gets passed to a variety of other functions but the user will never deref the pointer. Also, this pointer is not threadsafe (use amongst multiple threads would require a mutex). The naive Rust representation of this type would be..
struct ErlNifEnv;
*mut ErlNifEnv;
But, I think I can treat this type as having “interior mutability” which would lead to…
struct ErlNifEnv;
*const ErlNifEnv;
Should I be treating this pointer as const even though the underlying C code sees it as non-const?
I personally think that you should treat them as you treat references in Rust itself. When a function requires &mut
reference, it is very likely that it will actually change the value, and if it requires &
, then it is natural to expect that the value won't be changed (of course, disregarding inner mutability).
C does not have a distinction between inherited and inner mutability, and so you can choose what to use *mut
or *const
solely based on how C functions work with this value. In fact, in properly written C API this distinction will be present in form of const
qualifiers. Consequently, if there are any functions which want to mutate the value, go with *mut
. If there are no such functions (e.g. this is an immutable structure which is created only once) go with *const
. This is, of course, applies to the type you want to store in your wrapper - in FFI function signatures you should always mirror C API signatures.