Skip to content

Write a basic "call Rust from C" example #296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 20, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 68 additions & 18 deletions src/ffi.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,74 @@ the release of these resources (especially in the case of panic).

For more about destructors, see the [Drop trait](../std/ops/trait.Drop.html).

## Calling Rust code from C

You may wish to compile Rust code in a way so that it can be called from C.
This is fairly easy, but requires a few things.

### Rust side

First, we assume you have a lib crate named as `rust_from_c`.
`lib.rs` should have Rust code as following:

```rust
#[no_mangle]
pub extern "C" fn hello_from_rust() {
println!("Hello from Rust!");
}
# fn main() {}
```

The `extern "C"` makes this function adhere to the C calling convention, as discussed above in "[Foreign Calling Conventions]".
The `no_mangle` attribute turns off Rust's name mangling, so that it has a well defined symbol to link to.

Then, to compile Rust code as a shared library that can be called from C, add the following to your `Cargo.toml`:

```toml
[lib]
crate-type = ["cdylib"]
```

(NOTE: We could also use the `staticlib` crate type but it needs to tweak some linking flags.)

Run `cargo build` and you're ready to go on the Rust side.

[Foreign Calling Conventions]: ffi.md#foreign-calling-conventions

### C side

We'll create a C file to call the `hello_from_rust` function and compile it by `gcc`.

C file should look like:

```c
int main() {
hello_from_rust();
return 0;
}
```

We name the file as `call_rust.c` and place it on the crate root.
Run the following to compile:

```sh
gcc call_rust.c -o call_rust -lrust_from_c -L./target/debug
```

`-l` and `-L` tell gcc to find our Rust library.

Finally, we can call Rust code from C with `LD_LIBRARY_PATH` specified:

```sh
$ LD_LIBRARY_PATH=./target/debug ./call_rust
Hello from Rust!
```

That's it!
For more realistic example, check the [`cbindgen`].

[`cbindgen`]: https://github.com/eqrion/cbindgen

## Callbacks from C code to Rust functions

Some external libraries require the usage of callbacks to report back their
Expand Down Expand Up @@ -648,24 +716,6 @@ void register(void (*f)(int (*)(int), int)) {

No `transmute` required!

## Calling Rust code from C

You may wish to compile Rust code in a way so that it can be called from C. This is
fairly easy, but requires a few things:

```rust
#[no_mangle]
pub extern "C" fn hello_rust() -> *const u8 {
"Hello, world!\0".as_ptr()
}
# fn main() {}
```

The `extern "C"` makes this function adhere to the C calling convention, as
discussed above in "[Foreign Calling
Conventions](ffi.html#foreign-calling-conventions)". The `no_mangle`
attribute turns off Rust's name mangling, so that it is easier to link to.

## FFI and panics

It’s important to be mindful of `panic!`s when working with FFI. A `panic!`
Expand Down