Extern C Gets Old, Fast
It's not really a big deal for "hello world" - but once you start linking with
larger C libraries, you'll really start to hate writing extern "C" { ... } blocks
with all the function signatures.
Let's pretend that Hello World is a real library, and make add hello.h
(next to hello.c in the src directory):
#ifndef HELLO_H
#define HELLO_H
void say_hello(); /* Function prototype for say_hello() */
#endif
Yes, I know that
#pragma onceis a great way to start fights in the C++ community.
We'll use a tool named bindgen to read the header file and build the Rust for us.
Let's add bindgen to our build dependencies:
[workspace.dependencies]
cc = "1"
bindgen = "0"
Bindgen is proof that some projects never get to version 1. It's been 0.x since 2014!
Now we can update build.rs to generate the bindings for us:
use std::env; use std::path::PathBuf; fn main() { // Read the header let bindings = bindgen::Builder::default() .header("src/hello.h") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() .expect("Unable to generate bindings"); // Emit the bindings let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); bindings .write_to_file(out_path.join("hello.rs")) .expect("Couldn't write bindings!"); // Build the C code cc::Build::new() .file("src/hello.c") .compile("hello"); }
And update main.rs to use the generated bindings. Replace the entire extern "C" block with:
#![allow(unused)] fn main() { include!(concat!(env!("OUT_DIR"), "/hello.rs")); }
Now, on every compilation the hello.h will be turned into Rust (which is really
handy if the C might change) and bindings generated.
Nestled somewhere in your target directory, you'll find a hello.rs file that
looks like this:
#![allow(unused)] fn main() { /* automatically generated by rust-bindgen 0.70.1 */ extern "C" { pub fn say_hello(); } }
Stuck?
If it didn't compile, you might need to install clang and llvm on your system.
Go to: https://rust-lang.github.io/rust-bindgen/requirements.html for a guide.
There's also a Dockerfile available:
cd workshops/
docker build -t ex02 -f Dockerfile.ex02 .
docker run -it ex02