Rust closure as C-compatible function and env pointers pair
For a little project of mine I need some numerical integration routine. Rust does not have native math library yet, so I decided to bind to some already written C library, namely to GSL.
GSL integration facilities accept a pointer to a struct gsl_function_struct, defined like this:
struct gsl_function_struct
{
double (* function) (double x, void * params);
void * params;
};
That is, it is a pointer to a function and an additional parameter to that function.
This is obviously a closure-like structure: a function and an arbitrary environment, so supposedly high-level closures should fit into such structure nicely.
Is it possible to create such a structure out of Rust closure? As far as I know, Rust closures are exactly a function+env pointers pair. Can I convert them to GSL representation then? I tried to dig through rustc code, but to no avail, understanding it is well above my abilities :)
7 comments · 73% upvoted · other discussions
Yes, a Rust closure is essentially the same thing. Theoretically, you can transmute a
|...|to a struct/tuple of function pointer & environment pointer just likegsl_function_struct(and vice versa) to construct & deconstruct closures manually. (stdprovides therawmodule to make manipulating low-level representations like this safer, e.g.Closurefor|...|).However, the calling convention of a Rust function (including the underlying function of a
|...|) is not the same as that of a C function, in general. So converting a Rust closure to a GSL closure is liable to cause weird platform-specific corruption, due to arguments being passed from C to Rust differently to what Rust is expecting (and vice versa).dpx-infinity [OP] 2 points · Apr 2014
Great! I completely forgot about
std::rawmodule. That's exactly what I need. I believe can create a singledouble (* function) (double x, void * params)function and pass the closure data to it, so it would call the closure normally. Thanks!dbaupp 3 points · Apr 2014 · rust
If you mean something like
then yes, I guess it would work.
Anything that tries to pass the Rust-closure's function pointer to GSL as the
gsl_function_structs function pointer hits the calling convention issues I mentioned above (that's what theextern "C"function is for, interfacing between the world of C functions and the world of Rust functions). I urge you to avoid passing a destructured Rust closure directly to C... basically: forget I mentionedstd::rawat all.(If you do take the route above, I would strongly recommend defining a wrapper struct for
gsl_function_structthat uses theContravariantLifetimemarker to connect the input closure with the output GSL closure i.e..)
dpx-infinity [OP] 1 points · Apr 2014
Yeah, I did something like your example, but I've used
std::raw::Closureas an intermediate representation, so I didn't have to take a closure by reference. Now when I see what you have written I'm not sure if I did it right, but it does work and it does produce correct results.dbaupp 1 points · Apr 2014 · rust
You still don't need to handle
rawat all to avoid taking the closure by reference; just do the same&thing you've done to araw::Closuredirectly to the original|f64| -> f64.dpx-infinity [OP] 1 points · Apr 2014
Thanks, it did work, though in
integrandI had to use&mut |f64| -> f64instead of raw|f64| -> f64|. It did make code more clear anyway.dpx-infinity [OP] 6 points · Apr 2014
Indeed it does work :) it was easier than I thought. If someone is interested, I've got something like this: