Foreign-Clang-Plugin solution, such as solving rust and go two-way calls.
| ⇊Caller \ Callee⇉ | Go | Rust | |-------------------|:--:|:----:| | Go | - | ✅ | | Rust | ✅ | - |
shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default nightly
Download Go
Version go≥1.18
Set environment variables:
CGO_ENABLED=1
Use protoc v23.2
Take Protobuf IDL serialization solution as an example.
See the echo_pb
Generally, Fcplug is executed in a Crate's build.sh, and the code is automatically generated to the current Crate.
shell
cargo new --lib {crate_name}
```toml [lib] crate-type = ["rlib", "staticlib"]
[dependencies] fcplug = "0.3" pilota = "0.7.0" serde = "1" serde_json = "1"
[build-dependencies] fcplug-build = "0.3" ```
Write the IDL file {ffiname} .proto in ProtoBuf format, you can put it in the root directory of {cratename}, the content example is as follows:
```protobuf 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) {} } ```
build.rs
```rust
use fcplugbuild::{Config, generatecode, UnitLikeStructPath};
fn main() { generatecode(Config { idlfile: "./echo.proto".into(), // go command dir, default to find from $GOROOT > $PATH gorootpath: None, gomodparent: "github.com/andeya/fcplug/samples", targetcratedir: None, }) .unwrap(); } ```
```shell cargo build
cargo test
and cargo install
will also trigger the execution of build.rs to generate code```
lib.rs
file```rust
pub use echo_gen::*; use fcplug::{GoFfiResult, TryIntoTBytes}; use fcplug::protobuf::PbMessage;
mod echo_gen;
impl RustFfi for FfiImpl {
fn echors(mut req: ::fcplug::RustFfiArg
impl GoFfi for FfiImpl {
unsafe fn echogosetresult(mut goret: ::fcplug::RustFfiArg
```go package main
import ( "fmt"
"github.com/andeya/fcplug/samples/echo"
"github.com/andeya/gust"
)
func init() {
// TODO: Replace with your own implementation, then re-execute cargo build
GlobalGoFfi = GoFfiImpl{}
}
type GoFfiImpl struct{}
func (g GoFfiImpl) EchoGo(req echo.TBytes[echo.Ping]) gust.EnumResult[echo.TBytes[echo.Pong], ResultMsg] { fmt.Printf("go receive req: %v\n", req.PbUnmarshalUnchecked()) return gust.EnumOkecho.TBytes[echo.Pong], ResultMsg } ```
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
```bash
cargo build --release cargo build --release cargo build --release ```
lib.rs
,
```rust
extern crate test;
mod echo_ffi;
mod tests { use test::Bencher;
use fcplug::protobuf::PbMessage;
use fcplug::TryIntoTBytes;
use crate::echo_ffi::{FfiImpl, GoFfiCall, Ping, Pong};
#[test]
fn test_call_echo_go() {
let pong = unsafe {
FfiImpl::echo_go::<Pong>(Ping {
msg: "this is ping from rust".to_string(),
}.try_into_tbytes::<PbMessage<_>>().unwrap())
};
println!("{:?}", pong);
}
#[bench]
fn bench_call_echo_go(b: &mut Bencher) {
let req = Ping {
msg: "this is ping from rust".to_string(),
}
.try_into_tbytes::<PbMessage<_>>()
.unwrap();
b.iter(|| {
let pong = unsafe { FfiImpl::echo_go::<Vec<u8>>(req.clone()) };
let _ = test::black_box(pong);
});
}
} ```
go_call_rust_test.go
in the root directory,
```go package echo_test
import ( "testing"
"github.com/andeya/fcplug/samples/echo"
)
func TestEcho(t testing.T) { ret := echo.GlobalRustFfi.EchoRs(echo.TBytesFromPbUncheckedecho.Ping) if ret.IsOk() { t.Logf("%#v", ret.PbUnmarshalUnchecked()) } else { t.Logf("fail: err=%v", ret.AsError()) } ret.Free() }
```
```rust use fcplug::protobuf::PbMessage; use fcplug::TryIntoTBytes; use tokio::task;
use crate::echo_ffi::{FfiImpl, GoFfiCall, Ping, Pong};
let pong = task::spawnblocking(move | | { // The opened task runs in a dedicated thread pool. // If this task is blocked, it will not affect the completion of other tasks unsafe { FfiImpl::echogo::< Pong > (Ping { msg: "this is ping from rust".tostring(), }.tryinto_tbytes::< PbMessage < _ > > ().unwrap()) } }).await?;
```
in development
text
goos: darwin
goarch: amd64
pkg: github.com/andeya/fcplug/demo
cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz