blob: 943642ac79c460798edc092d417f360c953e3bac [file] [log] [blame] [view] [edit]
# A little Rust with your C
Using Rust code inside a C or C++ project mostly consists of two parts.
- Creating a C-friendly API in Rust
- Embedding your Rust project into an external build system
Apart from `cargo` and `meson`, most build systems don't have native Rust support.
So you're most likely best off just using `cargo` for compiling your crate and
any dependencies.
## Setting up a project
Create a new `cargo` project as usual.
There are flags to tell `cargo` to emit a systems library, instead of
it's regular rust target.
This also allows you to set a different output name for your library,
if you want it to differ from the rest of your crate.
```toml
[lib]
name = "your_crate"
crate-type = ["cdylib"] # Creates dynamic lib
# crate-type = ["staticlib"] # Creates static lib
```
## Building a `C` API
Because C++ has no stable ABI for the Rust compiler to target, we use `C` for
any interoperability between different languages. This is no exception when using Rust
inside of C and C++ code.
### `#[no_mangle]`
The Rust compiler mangles symbol names differently than native code linkers expect.
As such, any function that Rust exports to be used outside of Rust needs to be told
not to be mangled by the compiler.
### `extern "C"`
By default, any function you write in Rust will use the
Rust ABI (which is also not stabilized).
Instead, when building outwards facing FFI APIs we need to
tell the compiler to use the system ABI.
Depending on your platform, you might want to target a specific ABI version, which are
documented [here](https://doc.rust-lang.org/reference/items/external-blocks.html).
---
Putting these parts together, you get a function that looks roughly like this.
```rust
#[no_mangle]
pub extern "C" fn rust_function() {
}
```
Just as when using `C` code in your Rust project you now need to transform data
from and to a form that the rest of the application will understand.
## Linking and greater project context.
So then, that's one half of the problem solved.
How do you use this now?
**This very much depends on your project and/or build system**
`cargo` will create a `my_lib.so`/`my_lib.dll` or `my_lib.a` file,
depending on your platform and settings. This library can simply be linked
by your build system.
However, calling a Rust function from C requires a header file to declare
the function signatures.
Every function in your Rust-ffi API needs to have a corresponding header function.
```rust
#[no_mangle]
pub extern "C" fn rust_function() {}
```
would then become
```C
void rust_function();
```
etc.
There is a tool to automate this process,
called [cbindgen] which analyses your Rust code
and then generates headers for your C and C++ projects from it.
[cbindgen]: https://github.com/eqrion/cbindgen
At this point, using the Rust functions from C
is as simple as including the header and calling them!
```C
#include "my-rust-project.h"
rust_function();
```