Just got on to some of your videos recently, thanks for making them! Unfortunately my job is in a rust-prohibitive ecosystem but in my own time the rust learning has been magic
Hi! Great video and love the content. One quesstion I had is how can we implement a user provided callback that we can then pass it to a C function. For example I'm working on a C Lib that's defined something like this: void my_c_function(FnCallback function_cb, void *ptr) here FnCallback is a typedef for a function pointer that function takes `void *ptr` as an argument which is user provided data. So the way I would do it is: The user provides his function defined in pure Rust (the signature is defined by me so I know the type I'm expecting), I Box the to allocate that on the heap, I take the raw pointer to that Box and pass it as user data (void * ptr) to `c_function`, then inside the extern "C" function_cb I would construct the Boxed function again and call the Rust function from inside the extern "C". However this seems more like a "hack" than a proper way of doing things. Are there better ways?
You can use extern "C" functions and function pointers. So basically, you define a function like this: extern "C" fn callback_for_c() { println!("Callback called!"); } fn main() { // Pass the function pointer to your C function my_c_function(callback_for_c); }
Can't you just use non_exhaustive types or add a private member instead of using empty enums for opaque types? Tbh I'm kinda surprised that even works properly since an empty enum is more or less equivalent to the never type so I'd think the compiler considers it impossible for any such values to exist and would optimize accordingly. I guess if it's always behind a pointer it doesn't make a difference and there's not really anything else you can do wrong but it still seems very odd to me.
I'm honestly surprised Jon recommended using empty enums as a stand-in for opaque types, as that pattern has been discouraged for quite some time and most recommend using zero-size types instead (preferably with a private ZST member to prevent accidental construction). IIRC the primary motivator is that while raw pointers to uninhabited types are fine, references to uninhabited types are very much not fine. And unfortunately it can be pretty easy to accidentally create references to values in unsafe code even when you only intend to work with pointers to them. Creating a reference to your opaque type isn't something you'll actually want to do in your FFI code, but at least the consequences of that happening on accident are less severe when the result is the equivalent of `&()` rather than `&!`. Ideally Rust would stabilize the extern types feature at some point so that we don't have to use either of these workarounds, but until then we work with what we got.
Watching this at night, I appreciate your care to keep the s reen dark on top of the excellent content!
Just got on to some of your videos recently, thanks for making them! Unfortunately my job is in a rust-prohibitive ecosystem but in my own time the rust learning has been magic
Hey Jon, I bought the rustaceans book, and I am loving it
That was so helpful! I just started using soft and was so overwheld!
Thank you Jon! I've been needing to dig into these topics
definitely diving into your videos. Thank you so much for taking the ti to teach us that are green in the field. Have a great day
If the empty enum for opaque types is a common pattern, should rust consider introducing a less hacky way to do it? Maybe "pub opaque type_name;"
The "Extern Type" RFC is doing exactly this. But it has been unstable for years now.
Hi! Great video and love the content. One quesstion I had is how can we implement a user provided callback that we can then pass it to a C function. For example I'm working on a C Lib that's defined something like this: void my_c_function(FnCallback function_cb, void *ptr) here FnCallback is a typedef for a function pointer that function takes `void *ptr` as an argument which is user provided data. So the way I would do it is: The user provides his function defined in pure Rust (the signature is defined by me so I know the type I'm expecting), I Box the to allocate that on the heap, I take the raw pointer to that Box and pass it as user data (void * ptr) to `c_function`, then inside the extern "C" function_cb I would construct the Boxed function again and call the Rust function from inside the extern "C". However this seems more like a "hack" than a proper way of doing things. Are there better ways?
You can use extern "C" functions and function pointers. So basically, you define a function like this:
extern "C" fn callback_for_c() {
println!("Callback called!");
}
fn main() {
// Pass the function pointer to your C function
my_c_function(callback_for_c);
}
My brother, thanks for the stream, it makes my day brighter
Mine too. I wish he used dark mode for that
I must say, the way you move around the code, terminal, and documentation is simply masterful. How long did it take you to learn this power?
He was there when they wrote the rust compiler
"I was there, Gandalf. I was there 3000 years ago"
Great one, Jon. Thanks a lot!!
Thanks a lot Jon.
I love your content.
API/ABI incompatible changes in C are big pain as well.
Hi! If you have a dep that you want to compile with -fPIE, how do you pass it from your package? Using a feature? Or how so
?
Thanks a lot.
Thanks!
Can't you just use non_exhaustive types or add a private member instead of using empty enums for opaque types? Tbh I'm kinda surprised that even works properly since an empty enum is more or less equivalent to the never type so I'd think the compiler considers it impossible for any such values to exist and would optimize accordingly. I guess if it's always behind a pointer it doesn't make a difference and there's not really anything else you can do wrong but it still seems very odd to me.
I'm honestly surprised Jon recommended using empty enums as a stand-in for opaque types, as that pattern has been discouraged for quite some time and most recommend using zero-size types instead (preferably with a private ZST member to prevent accidental construction).
IIRC the primary motivator is that while raw pointers to uninhabited types are fine, references to uninhabited types are very much not fine. And unfortunately it can be pretty easy to accidentally create references to values in unsafe code even when you only intend to work with pointers to them. Creating a reference to your opaque type isn't something you'll actually want to do in your FFI code, but at least the consequences of that happening on accident are less severe when the result is the equivalent of `&()` rather than `&!`.
Ideally Rust would stabilize the extern types feature at some point so that we don't have to use either of these workarounds, but until then we work with what we got.
He's back!!!
Yeeeessssss
I feel like there is gonna be a r/whoosh joke here