From 64553aa569e781db46dfa5508b8913cdd999091c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 28 Jul 2021 19:32:14 +0900 Subject: [PATCH 1/4] Write a basic "call Rust from C" example --- src/ffi.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/src/ffi.md b/src/ffi.md index 825be5a..f3d6c1e 100644 --- a/src/ffi.md +++ b/src/ffi.md @@ -650,21 +650,68 @@ 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: +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_rust() -> *const u8 { - "Hello, world!\0".as_ptr() +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](ffi.html#foreign-calling-conventions)". The `no_mangle` -attribute turns off Rust's name mangling, so that it is easier to link to. +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 is easier 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"] +``` + +Run `cargo build` and you're ready to go on the Rust side. + +[Foreign Calling Conventions]: ffi.html#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 +``` + +That's it! +For more realistic example, check the [`cbindgen`]. + +[`cbindgen`]: https://github.com/eqrion/cbindgen ## FFI and panics From 044051c0edc4c58bfbc96f761c4032c124ee89b6 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 28 Jul 2021 19:33:17 +0900 Subject: [PATCH 2/4] Move the section higher --- src/ffi.md | 130 ++++++++++++++++++++++++++--------------------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/src/ffi.md b/src/ffi.md index f3d6c1e..f9a7812 100644 --- a/src/ffi.md +++ b/src/ffi.md @@ -240,6 +240,71 @@ 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 is easier 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"] +``` + +Run `cargo build` and you're ready to go on the Rust side. + +[Foreign Calling Conventions]: ffi.html#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 +``` + +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 @@ -648,71 +713,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 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 is easier 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"] -``` - -Run `cargo build` and you're ready to go on the Rust side. - -[Foreign Calling Conventions]: ffi.html#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 -``` - -That's it! -For more realistic example, check the [`cbindgen`]. - -[`cbindgen`]: https://github.com/eqrion/cbindgen - ## FFI and panics It’s important to be mindful of `panic!`s when working with FFI. A `panic!` From 543c9b1bd76151616a7fc39727fc3030cdf107ee Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 28 Jul 2021 19:35:06 +0900 Subject: [PATCH 3/4] Show the output --- src/ffi.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ffi.md b/src/ffi.md index f9a7812..0f03fa7 100644 --- a/src/ffi.md +++ b/src/ffi.md @@ -297,7 +297,8 @@ gcc call_rust.c -o call_rust -lrust_from_c -L./target/debug Finally, we can call Rust code from C with `LD_LIBRARY_PATH` specified: ```sh -LD_LIBRARY_PATH=./target/debug ./call_rust +$ LD_LIBRARY_PATH=./target/debug ./call_rust +Hello from Rust! ``` That's it! From 88b2dbcce66090d5e0441770e1dea3cf7c93b898 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 17 Sep 2021 13:50:49 +0900 Subject: [PATCH 4/4] Apply review comments --- src/ffi.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ffi.md b/src/ffi.md index 0f03fa7..3f41187 100644 --- a/src/ffi.md +++ b/src/ffi.md @@ -259,7 +259,7 @@ pub extern "C" fn hello_from_rust() { ``` 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 is easier to link to. +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`: @@ -268,9 +268,11 @@ Then, to compile Rust code as a shared library that can be called from C, add th 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.html#foreign-calling-conventions +[Foreign Calling Conventions]: ffi.md#foreign-calling-conventions ### C side