Writing bindings in Rust: Part 2
28 Jan 2017  |  rust, libevdevIn 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.
Let’s begin
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 from libevdev_new
according to rust’s naming conventions. Also notice that we are returning Option<Device>
instead of Device
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 Device
.
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 str
to char *
and vice
versa. To get a char *
from str
we use libc::CString
i.e.
The code for the other way around is slightly bigger so we better declare a function to convert from char *
to str
.
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: