TL;DR:
Is it possible to prevent user use a type T
in ffi interfaces, while allowing user use T
elsewhere?
I means, since T
might have the same #[repr]
as U
but with Drop
implemented, users may wrote extern "C" fn (U)
to extern "C" fn (T)
accidently, how to prevent it?
To avoid a XY problem, the background of such silly question is provided as follow:
This could be a ffi problem, which occurs in my rmin crate.
The crate, rmin, are designed to help with R-plugin writting. The basic FFI interface could be something like:
extern "C-unwind" fn (a:SEXP)->SEXP { ... }
According to R documents,
- The parameter R passed in is read-only.
- Each time call R function, the allocated variable might be recycled by R unless we protected it.
- Before the program return, we must unprotect all of the allocated variables.
With these restrictions,
SEXP<T>
is a readonly type that does not implIndexMut
, and have the same repr asSEXP
Owned<T>
is a owned type thus it implsIndexMut
, and have the same repr asSEXP
Protected<T>
is a protectedOwned<T>
, we must not send nor receiveProtected<T>
from ffi interface.
With these 3 types, we could wrote a healthy function like
extern "C" fn add(a:SEXP<f64>, b:SEXP<f64>)->Owned<f64> {
let mut c=Owned::new(1);
c[0]=a[0]+b[0]; // I have `Index` and `IndexMut` implememted
c
}
The question is, how to achieve 3. gracefully? I means, how to prevent users wrote things like
extern "C" fn add(a:SEXP<f64>, b:SEXP<f64>)->Protected<f64> {
let mut c=Protected::new(1);
c[0]=a[0]+b[0]; // I have `Index` and `IndexMut` implememted
c // currently, Protected<f64> is send back to R, without calling its `drop` function, which breaks 3.
}
Currently, I tried the visibility, wrote things like:
#[repr(transparent)]
pub struct Owned<T:RType>{
sexp:libR::SEXP,
_marker:[PhantomData<T>;0]
}
impl<T:RType> Owned<T> {
#[allow(private_interfaces)]
pub fn protect(self)->Protected<T> {
self.into()
}
}
mod protected {
/// it seems Protected have no doc.
#[repr(transparent)]
pub struct Protected<T:RType>{
sexp:libR::SEXP,
_marker:[PhantomData<T>;0]
}
}
Although protect
interface pub fn protect(self)->Protected<T>
generate no warning about private_interfaces, there is no doc for the Protected
type. What makes it worse, we cannot write things like let a=Protected::new(1)
, and we have to write let a=Owned::new(1).protected()
.
What could be the best way for hiding such struct?
Update: with macro 2.0, we can play with hygiene macro which prevent writting Protected<T>
accidently, without removing its documents.
Just use such grammar could be fine:
// the redundant `Key1=` is a checker to show whether we use the correct parameter.
macro visible_controller(Key1=$Key1:tt Key2=$Key2:tt ...) {
// things with $Key will be visible outside
struct $Key1();
// things without $ will be invisible
struct Protected();
}
visible_controller!(Key1=Key1 Key2=Key2);
// Protected only exists in the doc, but keep invisible from users.