Rust2Go is a project that provides users with a simple and efficient way to call Golang from Rust with native async support. It also support user calling Rust from Golang.
- Sync and async calls from Rust to Golang
- Sync calls from Golang to Rust
- Efficient data exchange: no serialization or socket communication, but FFI
- Simple interface design: no new invented IDL except for native rust
- Define the structs and calling interfaces in restricted Rust syntax, and include generated code in the same file.
- Generate golang code with
rust2go-cli --src src/user.rs --dst go/gen.go- Use
--package-name <name>to set the package name of the generated go file (defaults tomain). - Use
--without-mainto omit the go main function,--go118for Go 1.18/1.19 compatibility, and--no-fmtto skip formatting the generated file.
- Use
- Write a
build.rsfor you project (see docs/build-rs.md for the full build script helper reference, including dynamic linking and custom go build arguments). - You can then use generated implementation to call golang in your Rust project!
For detailed example, please checkout the example projects.
- Supported types:
i8/i16/i32/i64/isize,u8/u16/u32/u64/usize,f32/f64,bool,char,String,Vec<T>, user-defined structs, and non-generic type aliases (e.g.pub type Amount = i64;, expanded during code generation).Option<T>is treated asVec<T>:Nonemaps to an empty list on the Go side. - Trait functions may take zero, one or multiple parameters; empty (nil) slices are allowed as arguments and return values.
- Structs keep their own attribute macros (e.g.
#[derive(...)]) in the generated code, and#[rust2go::r2g_struct_tag(json = "snake_case")]adds tags to the generated Go struct fields. See docs/trait-attrs.md for the full attribute reference.
Detailed design details can be found in this article: Design and Implementation of a Rust-Go FFI Framework.
- Memory layout: Rust2go only manipulates memory when needed. In most cases it passes memory reference.
- Message passing: Rust2go relies on CGO to pass calling information. In addition, it also supports lock-free queues based on shared memory to improve performance during high-frequency communication.
- Other optimizations: Rust2go uses Go callback based on manual assembly instead of CGO to achieve better performance.
In order to achieve the ultimate performance, this project is not purely based on communication, but on FFI to pass specially encoded data. In order to reduce memory operations to a minimum, data that satisfies a specific memory layout is passed directly by reference rather than copied.
For example, Vec<u8> and String is represented as a pointer and a length. However, structs like Vec<String> or Vec<Vec<u8>> require intermediate representation. In order to reduce the number of memory allocations to one, I use a precomputed size buffer to store these intermediate structures.
On the Golang side, the data it receives is referenced from Rust. The Rust side will do its best to ensure the validity of this data during the call. So the Golang side can implement the handler arbitrarily, but manually deep copy when leaking data outside the function life cycle.
On the Rust side, it is needed to ensure that the slot pointer of the callback ffi operation, and the user parameters are valid when the future drops. This is archieved by implementing an atomic slot structure and providing a [drop_safe] attribute to require user passing parameters with ownership.
Note: Since golang may scan the stack, and when it meets peer pointer, it may panic. You should run the program with GODEBUG=invalidptr=0,cgocheck=0 env to bypass it.
- Golang: >=1.18
- For >=1.18 && < 1.20: generate golang code with
--go118 - For >=1.20: generate golang code normally
- For >=1.18 && < 1.20: generate golang code with
- Rust: >=1.75 if you want to use async; crates using rust2go may use edition 2021 or 2024 (edition 2024 requires Rust >=1.85)
- Linux, macOS and Windows are supported.
- The ASM-based callback is available on amd64 and arm64; on other platforms it falls back to the CGO implementation automatically.
- The shared memory based implementation (
#[mem]/#[shm]) requires unix.
- IDL(in rust) parse
- Go code generation
- Build script helper
- Basic data types and convertion generation
- Rust impl generation
- Future and basic synchronization primitives used
- More complicated data types support
- Support user passing references
- More elegant code generation implementation
- Better build cache control
- Golang interface support(separate user code from generated code)
- Dynamic linking support
- Golang helper library
- Shared memory based implementation
- Faster ASM-based callback instead of CGO
- Support calling rust from golang
This project is inspired by fcplug.