fcplug
Foreign-Clang-Plugin solution, such as solving rust and go two-way calls.
Features
⇊Caller \ Callee⇉ | Go | Rust |
---|---|---|
Go | - | ✅ |
Rust | ✅ | - |
- Protobuf IDL codec solution: Supported!
- Thrift IDL codec solution: In development...
- No codec solution: In development...
Schematic
Prepare
- Install rust nightly
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default nightly
- Install go
Download Go
Version go≥1.18
Set environment variables:
CGO_ENABLED=1
- Install protoc
Use protoc v23.2
go install github.com/golang/protobuf/protoc-gen-go@v1.5.3
Example of use
Take Protobuf IDL serialization solution as an example.
See the echo_pb
Step 1: create/prepare a crate
Generally, Fcplug is executed in a Crate's build.sh, and the code is automatically generated to the current Crate.
- If you do not have a Crate, execute the following command to create it:
cargo new --lib {crate_name}
- Add
staticlib
crate-type and some dependent packages, open debug log ofbuild.rs
, edited in Cargo.toml as follows:
[]
= ["rlib", "staticlib"]
[]
= 0
= true
[]
= "0.3"
= "0.7.0"
= "1"
= "1"
[]
= "0.3"
Step 2: Write the IDL file that defines the FFI interface
Write the IDL file {ffi_name} .proto in ProtoBuf format, you can put it in the root directory of {crate_name}, the content example is as follows:
syntax = "proto3";
message Ping {
string msg = 1;
}
message Pong {
string msg = 1;
}
// go call rust
service RustFFI {
rpc echo_rs (Ping) returns (Pong) {}
}
// rust call go
service GoFFI {
rpc echo_go (Ping) returns (Pong) {}
}
Step 3: Scripting auto-generated code build.rs
use ;
Step 4: Preliminary Code Generation
- Execute under the current Crate:
cargo build
# `cargo test` and `cargo install` will also trigger the execution of build.rs to generate code
- Attach the generated src/{ffi_name}_ffi mod to Crate, that is, add mod {ffi_name}_ffi to the
lib.rs
file
Step 5: Implement the FFI interface
- On the rust side, you need to implement the specific trait RustFfi and trait GoFfi methods in the newly initialized file src/{ffi_name}_ffi/mod.rs. The complete sample code of the file is as follows:
pub use *;
use ;
use PbMessage;
- Implement the go GoFfi interface in the one-time generated file ./cgobin/clib_goffi_impl.go. The complete sample code of this file is as follows:
package main
import (
"fmt"
"github.com/andeya/fcplug/samples/echo_pb"
"github.com/andeya/gust"
)
func init()
type GoFfiImpl struct
func (req echo_pb.TBytes[echo_pb.Ping]) gust.EnumResult[echo_pb.TBytes[*echo_pb.Pong], ResultMsg]
Step 6: Generate Final Code
Execute cargo build
cargo test
or cargo install
under the current Crate, trigger the execution of build.rs, and
generate code.
Note: When GoFfi is defined, after compiling or changing the code for the first time, a warning similar to the following will occur, and you should execute cargo build twice at this time
warning: ... to re-execute 'cargo build' to ensure the correctness of 'libgo_echo.a'
Therefore, it is recommended to repeat cargo build three times directly in the build.sh
script
#!/bin/bash
Step 7: Testing
- Rust calls Go tests, you can add test functions in
lib.rs
, the sample code is as follows:
extern crate test;
- Go calls Rust test, add the file
go_call_rust_test.go
in the root directory, the sample code is as follows:
package echo_pb_test
import (
"testing"
"github.com/andeya/fcplug/samples/echo_pb"
)
func TestEcho(t *testing.T)
Asynchronous programming
- Rust Tokio asynchronous function calling Go synchronous function
use PbMessage;
use TryIntoTBytes;
use fcplug- task;
use crate;
let pong = spawn_blocking.await?;
- Go calls Rust, at least one side is an async function
in development
Benchmark
goos: darwin
goarch: amd64
pkg: github.com/andeya/fcplug/demo
cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz