Decide union structure in Rust FFI

I have a problem with a resolving c-merged XEvent structure.

I am experimenting with Xlib and the X Record Extension in Rust. I generate ffi bindings with rust-bindgen . All code is hosted on github alxkolm / rust-xlib-record .

The problem is when running src / main.rs: 106 when I try to retrieve data from an XEvent structure.

 let key_event: * mut xlib :: XKeyEvent = event.xkey ();
 println! ("KeyPress {}", (* key_event) .keycode);  // this always print 128 on any key

My program listens for key events and prints keycode . But it is always 128 on any key that I press. I think this is a wrong conversion from a connection type C to a type Rust.

The XEvent definition begins here src / xlib.rs: 1143 . This is a c-union. The original definition of C is here .

GitHub code can be executed with the cargo run command. It compiles without errors.

What am I doing wrong?

+5
source share
1 answer

Beware that rustbindgen creates a binding to the C union with the same security as in C; as a result, when called:

 event.xkey(); // gets the C union 'xkey' field 

There is no runtime check that xkey is the field currently containing the value.

This is because, since C does not have a union label (i.e., union knowing which field is currently being used), the developers came up with various ways to encode this information (*), the ones I know of:

  • external provider; usually another structure field immediately before union
  • first field of each of the structures in union

Here you are in the latter case int type; - This is the first field of the union, and each nested structure begins with int _type; to denote it. As a result, you need a two-step approach:

  • consult type()
  • depending on the value, call the correct reinterpretation

Mapping from the type value to the actual field used should be part of the C library documentation, hopefully.

I invite you to come up with a wrapper around this low-level union , which will make it safer to get the result. At least you can verify that it is the right type in the accessory; A complete approach is to come up with a Rust enum that will wrap proxies in all fields and allow pattern matching.

(*) and actually sometimes even ignore it, for example, on C99, in order to rethink a float as int a union { float f; int i; } union { float f; int i; } union { float f; int i; } .

+4
source

Source: https://habr.com/ru/post/1206077/


All Articles