15 releases

0.3.10 Nov 6, 2024
0.3.9 Sep 5, 2024
0.3.8 Jun 29, 2024
0.3.6 Mar 14, 2024
0.0.2 Dec 10, 2023

#144 in FFI

Download history 131/week @ 2024-08-16 158/week @ 2024-08-23 426/week @ 2024-08-30 890/week @ 2024-09-06 849/week @ 2024-09-13 1426/week @ 2024-09-20 790/week @ 2024-09-27 995/week @ 2024-10-04 1187/week @ 2024-10-11 917/week @ 2024-10-18 1279/week @ 2024-10-25 963/week @ 2024-11-01 789/week @ 2024-11-08 582/week @ 2024-11-15 1142/week @ 2024-11-22 1097/week @ 2024-11-29

3,834 downloads per month
Used in 2 crates (via rust2go)

MIT/Apache

79KB
1.5K SLoC

Rust2Go

Crates.io

Rust2Go is a project that provides users with a simple and efficient way to call Golang from Rust with native async support.

Features

  • Sync and async calls from Rust to Golang
  • Efficient data exchange: no serialization or socket communication, but FFI
  • Simple interface design: no new invented IDL except for native rust

How to Use

  1. Define the structs and calling interfaces in restricted Rust syntax, and include generated code in the same file.
  2. Generate golang code with rust2go-cli --src src/user.rs --dst go/gen.go
  3. Write a build.rs for you project.
  4. You can then use generated implementation to call golang in your Rust project!

For detailed example, please checkout the example projects.

Key Design

Detailed design details can be found in this article: Design and Implementation of a Rust-Go FFI Framework.

Why Fast?

In order to achieve the ultimate performance, this project is not 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.

Memory Safety

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.

Toolchain Requirements

  • Golang: >=1.18
    • For >=1.18 && < 1.20: generate golang code with --go118
    • For >=1.20: generate golang code normally
  • Rust: >=1.75 if you want to use async

With my experience, starting from Golang 1.21 there is a significant performance improvement in CGO. So I recommend using Golang 1.21 or later.

Milestones

Init Version

  • 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

Basic Ability Enhancement

  • 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

Performance Optimization

  • Shared memory based implementation
  • Go-side reference passing support saving stack grow cost for big size data
  • Go-side object pool support saving allocation cost for complicated data types

Extended Features

  • Support calling rust from golang

Exploratory Features

  • Support sense peer memory layout at boot time or compile time and access fields directly

Credit

This project is inspired by fcplug.

Dependencies

~5–14MB
~200K SLoC