问题
I have problem with resolving c-union structure XEvent.
I'm experimenting with Xlib and X Record Extension in Rust. I'm generate ffi-bindings with rust-bindgen. All code hosted on github alxkolm/rust-xlib-record.
Trouble happen on line src/main.rs:106 when I try extract data from XEvent structure.
let key_event: *mut xlib::XKeyEvent = event.xkey(); println!("KeyPress {}", (*key_event).keycode); // this always print 128 on any key
My program listen key events and print out keycode
. But it is always 128 on any key I press. I think this wrong conversion from C union type to Rust type.
Definition of XEvent starts here src/xlib.rs:1143. It's the c-union. Original C definition here.
Code from GitHub can be run by cargo run
command. It's compile without errors.
What I do wrong?
回答1:
Beware that rustbindgen
generates binding to C union
with as much safety as in C; as a result, when calling:
event.xkey(); // gets the C union 'xkey' field
There is no runtime check that xkey
is the field currently containing a value.
This is because since C does not have tagged union
(ie, the union
knowing which field is currently in use), developers came up with various ways of encoding this information (*), the two that I know of being:
- an external supplier; typically another field of the structure right before the
union
- the first field of each of the structures in the
union
Here, you are in the latter case int type;
is the first field of the union and each nested structure starts with int _type;
to denote this. As a result, you need a two-steps approach:
- consult
type()
- depending on the value, call the correct reinterpretation
The mapping from the value of type to the actual field being used should be part of the documentation of the C library, hopefully.
I invite you to come up with a wrapper around this low-level union
that will make it safer to retrieve the result. At the very least, you could check it is the right type in the accessor; the full approach being to come up with a Rust enum
that would wrap proxies to all the fields and allow pattern-matching.
(*) and actually sometimes disregard it altogether, for example in C99 to reinterpret a float
as an int
a union { float f; int i; }
can be used.
来源:https://stackoverflow.com/questions/26710596/resolve-union-structure-in-rust-ffi