Writing bindings in Rust: Part 228 Jan 2017
In the last post, we looked at how we can write a crate which allows raw access to C functions. In this post, we will write a safe abstraction for this crate. We will be using a part of evdev-sys as an example to see how to write the wrapper functions.
Why do we need a wrapper in the first place?
The wrapper is a way of writing a safe abstraction around an unsafe C function which would allow us to use rust specific constructs like ownership and borrowing to ensure memory safety. This abstraction also allows us to hide details of the bindings and the end user can use it like any other normal rust function. We can also introduce an API which is more object oriented than the one which provided by C library.
This is the lib.rs that we are going to use for the example:
We not only need to write safe abstractions but we also need to make the API object-oriented. Since most of the function take a libevdev device
pointer we can declare a
Device class and make all these functions its methods. We will have the
Device struct which has the raw pointer to
the libevdev device.
Notice that we have not made the raw pointer public as we want to avoid unsafe access to C pointer by the user. In rust, the methods for a struct are written in an impl block.
Let’s start with the
libevdev_new function. We need to call the function and then return a Device struct with the libevdev pointer. We will remove libevdev
libevdev_new according to rust’s naming conventions. Also notice that we are returning
Option<Device> instead of
because it is good rust code. Rust provides us with
Option<T> to deal with function in which we might not always return the required value.
libevdev_free needs to be executed once the use of the libevdev device is over. Rust provides us the
drop trait for this. We don’t need
to free the device ourselves instead we specify the code that needs to be executed once a value goes out of scope and rust will execute it
for us. We need to implement
libevdev_free in the drop trait for
Now’s let write a wrapper for a function with some arguments, most of the arguments types are easier to deal except for the Strings.
as comes in handy while converting between rust and c types. However, we need to put more effort to convert an
char * and vice
versa. To get a
char * from
str we use
The code for the other way around is slightly bigger so we better declare a function to convert from
char * to
We have almost completed our wrapper except for one last function which shows the use of enums in rust.
We have written a wrapper for evdev-sys. You can have a look at the repo for the complete bindings. Here are some references which I found useful: