Adrián Arroyo Calle

Instalando bindgen

sudo apt install llvm-3.9-dev libclang-3.9-dev clang-3.9

Modo línea de comandos

cargo install bindgen

bindgen /usr/include/sqlite3.h -o sqlite.rs

mod sqlite;



use sqlite::{sqlite3_open, sqlite3_exec, sqlite3_close, SQLITE_OK};

use std::ffi::CString;

use std::ptr::{null_mut,null};



fn main(){

let mut db = null_mut();

let database_name = CString::new("test.db").unwrap().into_raw();

let sql = CString::new("

CREATE TABLE contacts (name TEXT, tel TEXT);

INSERT INTO contacts VALUES ('Adrian','555-555-555');").unwrap().into_raw();

let mut error_msg = null_mut();

unsafe{

sqlite3_open(database_name,&mut db);

let rc = sqlite3_exec(db,sql,None,null_mut(),&mut error_msg);

if rc != SQLITE_OK as i32 {

let error = CString::from_raw(error_msg);

println!("ERROR: {}",error.into_string().unwrap());

}

sqlite3_close(db);

}

}

rustc main.rs -lsqlite3

Build.rs

[build-requires]

bindgen = "0.26.3"

#include <sqlite3.h>

extern crate bindgen;



use std::env;

use std::path::PathBuf;



fn main() {

// indicamos al linker que necesitamos sqlite3

println!("cargo:rustc-link-lib=sqlite3");





let bindings = bindgen::Builder::default()

.header("wrapper.h")

.generate()

.expect("Unable to generate bindings");



// escribir los bindings en $OUT_DIR/bindings.rs

let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());

bindings

.write_to_file(out_path.join("bindings.rs"))

.expect("Couldn't write bindings!");

}

#![allow(non_upper_case_globals)]

#![allow(non_camel_case_types)]

#![allow(non_snake_case)]



include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

Rust es un lenguaje con muchas posibilidades pero existe mucho código ya escrito en librerías de C y C++. Código que ha llevado mucho tiempo, que ha sido probado en miles de escenarios, software maduro y que no compensa reescribir. Afortunadamente podemos reutilizar ese código en nuestras aplicaciones Rust a través de bindings. Los bindings no son más que trozos de código que sirven de pegamento entre lenguajes. Esto es algo que se ha podido hacer desde siempre pero dependiendo de la librería podía llegar a ser muy tedioso. Afortunadamente tenemos bindgen , un programa que permite generar estos bindings de forma automática analizando el código de la librería de C o C++.En este post veremos como usardesde Rust usando bindgen.En primer lugar necesitamos tener instaladoo superior. En Ubuntu o Debian necesitamos estos paquetes:Para el resto de plataformas puedes descargar el binario desde la página de descargas de LLVM Bindgen permite dos modos de uso: línea de comandos o desde el código Rust. El más habitual es desde código Rust pero antes veremos el modo en línea de comandos.Para bindings sencillos podemos usar el modo línea de comandos. Instalamos binden con Cargo:Su uso es muy sencillo:Simplemente indicamos el fichero de cabecera que queremos traducir y su correspondiente fichero de salida en Rust. Este fichero será el pegamento. Vamos a crear un programa que use este pegamento:Como se puede apreciar, las llamadas al módulo de pegamento de hacen desde un bloqueya que se van a usar punteros al estilo C, de forma insegura. Hace tiempo escribí sobre ello así que voy a saltarme esa parte.Compilamos enlazando de forma manualde la siguiente forma:Si todo va bien, compilará aunque con numerosos warnings. En principio no son importantes.Ahora si ejecutamos el programa resultante debería crear una base de datos nueva con una tabla contacts y los datos insertados. ¡Hemos conseguido llamar a una librería de C desde Rust y no hemos escrito ningún binding!El sistema anterior funciona, pero no es lo más práctico, además no usa Cargo que es el sistema estándar de construcción de programas y crates un Rust. Lo habitual es dejar este proceso automatizado en el ficheroque se ejecuta con Cargo.Lo primero es añadir la siguiente línea al fichero Cargo.toml:El siguiente paso consiste en crear un archivo cabecera de C que a su vez haga referencia a todos los archivos de cabecera que necesitamos. En el caso de SQLite es bastante simple.Y lo llamamos wrapper.hAhora viene lo interesante. Dentro de build.rs creamos un programa que gracias a la API de bindgen haga lo mismo que la línea de comandos.El archivo build.rs debe estar en la misma carpeta que Cargo.toml para que funcione.Finalmente para hacer accesible nuestros bindings creamos un módulo llamado sqlite.rs con el siguiente contenido.Lo único que hace es desactivar unos warnings molestos e incluir el texto de bindings.rs al completo.Una vez hecho esto podemos usar desde el programa principal la librería de la misma forma que hemos visto antes.Ahora podríamos usar estos bindings directamente en nuestro programa o rustizarlos (darles una capa segura alrededor e idiomática) y subirlo a Crates.io.