Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I want to call C library "mysql.h" from Rust language

Tags:

c

rust

I'm trying to connect to mysql from rust code. I've tried these steps.

    1. I wrote c code using mysql.h, and command below.
    
     $ gcc -shared mysqlrust.c -o libmysqlrust.so  $(mysql_config --cflags) $(mysql_config --libs)   $(mysql_config --cflags)
     $ cp libmysqlrust.so /usr/local/lib/rustc/i686-unknown-linux-gnu/lib/
    
    2. I wrote Rust code, that calls libmysqlrust.so.

But I couldn't figure out way to use C type structure "MYSQL", "MYSQL_RES", "MYSQL_ROW". Please show me how to use c type structure from rust code.

like image 695
Takuya Horimatsu Avatar asked Feb 26 '12 03:02

Takuya Horimatsu


1 Answers

There is not yet any way to automatically create Rust type definitions from C structs. In these situations there are a few ways to proceed. Not knowing the MySQL API, I can't say exactly what you should do, but here are some options.

1) Treat them entirely as opaque pointers.

This is the best situation to be in, and depends on the C API always taking the struct as a pointer, having its own constructor and destructor functions, and providing accessor functions for whatever you need to access inside the struct. In these cases you just define type MYSQL = ctypes::void and only ever use it as an unsafe pointer *MYSQL. Sometimes the easiest path is to write your own C wrappers to fill in the gaps and make this scenario possible.

The remaining scenarios all involve redefining a Rust data structure with the same structure as the C struct. Rust tries to lay out its data structures in a way that is compatible with C (though doesn't always succeed yet), so it is often possible to create a Rust record or enum with the size, alignment and layout of the C struct you care about. You will want to make sure you use the types in core::ctypes, as they are defined to match various common C types.

Note that the ctypes module will be going away soon in favor of a more comprehensive libc compatibility module.

2) Define a Rust record that is partially correct.

If the API provides constructors and destructors, but you still need access to some fields of the struct, then you can define just enough of the struct to get at the fields you care about, disregarding things like the correct size and alignment. e.g. type MSQL = { filler1: ctypes::int, ..., connector_fd: *ctypes::char }. You can stop defining the struct at the last field you care about since you have a C function to allocate it on the heap with the correct size and alignment. In Rust code you always refer to it with an unsafe pointer: let mysql: *MYSQL = mysqlrust::create_mysql();

3) Define a Rust record that is the correct size and alignment, without caring about the contents.

If you don't have constructor/destructor functions, or need to store the struct on the stack, but you otherwise have accessor functions to manipulate the contents of the struct, then you need to define a Rust record with the correct size and alignment. To do this, just add fields of type uint (which is always pointer-sized) or tuples of uint, until both C's sizeof and core::sys::size_of agree on the size. Pad with u8s if the size isn't a multiple of the pointer size. Getting the alignment right is a more mystical process, but by using uint fields you will generally end up with a usable alignment (maybe - I really have no idea how accurate that statement is).

I would recommend adding tests to sanity check that Rust and C agree on the size in order to guard against future breakage.

3) Actually redefine the entire C struct.

This is a pretty dire situation for large structs, and it is possible in theory, but I don't think anybody has done it for a struct as big as MYSQL. I would avoid it if you can. Eventually there will be a clang-based tool to do this automatically.

Here are some examples of interop with C structs:

https://github.com/jdm/rust-socket/blob/master/socket.rs - This redefines various socket structs, adding placeholders for fields it doesn't care about. Note that it uses u8 for padding, but I think uint is more likely to produce correct alignment.

https://github.com/erickt/rust-zmq/blob/master/zmq.rs

https://github.com/pcwalton/rust-spidermonkey - This one demonstrates interop with a somewhat complex API.

like image 111
brson Avatar answered Sep 17 '22 19:09

brson