Did you know that Rust has been voted the 'most loved programming language' for five years in a row by the Stack Overflow community? It's no wonder why Rust has gained such popularity - its unique features make it a powerful tool for systems programming. But what if you want to use Rust in conjunction with another language? In this post, we'll explore how you can call Rust code from Go with a more "gambiarra" approach, and how this approach can be useful for migrating apps to Rust.
A more correct way.
Before we start we need to address the post that triggered this idea in me, is this a great post(RUSTGO: CALLING RUST FROM GO WITH NEAR-ZERO OVERHEAD) from Filippo Valsorda, I wanted to make a version with this but in a different(probably a way less performant version of it) but with the migration of projects in mind instead of having something living together, and calling with thought an FFI. The idea is simple and there is a repo with a minimal viable example here. Without further ado, to make this happen the only thing you will need to add to your rust code is clap if it does not have it, and yes, you probably see where we are going with this. We are making our Rust code a CLI program and using the thing every language that I know has, the ability to call other programs, something as we do in Go with exec.Command
from the os/exec
library.
How does this work?
Visual model of what is happening.
The steps you will need to follow are:
- Create entry points for your code with the parameters you need (clap will help a lot with this). Your rust code should return as output like text or it can write to a file that should be read from your main app program later. The
.Output
method will return a buffer with every single thing that was printed from your Rust program, if you expect from you rust code a more complex response, write to a file and read. - Compile your rust to an executable in the host machine, which can be done before starting the main app.
- In your main app, for example in Go, when you need to call this module/feature/thing-that-you-need-that-is-in-rust you make a terminal call to it using your main app and unmarshal it as it was another API call.
And there you go, you have yourself two living projects that can talk to each other. This can be really useful when migrating apps to Rust.
In practice
For to have at least an example of this in action I will show what I did in the example project:
Our Rust code is below:
use clap::Parser;
/// Simple program to greet a person
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Name of the person to greet
#[arg(short, long)]
name: String,
}
fn main() {
let args = Args::parse();
println!("Hello, {}!", args.name)
}
Is the example from clap as its starting point and well what we will do is compile this code using cargo build --release
and then in our Go code we will call this binary like this:
package main
import (
"fmt"
"os/exec"
)
func main() {
output, err := exec.Command("./rust-code/target/release/rust-code", "--name", "John").Output()
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(string(output)) // will print "Hello, John!"
}
Now we have something that works, this can be done in almost any language that compiles to an executable and it can be called within the command line. Sure that is not the most reliable way of doing things, as we do not have a great type system in our favor and we hurt performance as we are creating and making objects all the time but is the most straightforward way and in my opinion the easiest way to go from language x to language y.
Top comments (3)
Your example using
exec
you are calling binary, it would not matter if it was written in rust or any other languageone way to call "functions" of a language inside Go is using the plugin, where you load the operating system library (files
.so
) and don't execute the binary as in your example aboveI am using this plugin approach in prestd, here is a thread discussing how it works
Yes! this was mostly the example that steped upon, you are 100% correct, any compiled language should do the trick.
Never seen this before go-plugin, will check it out! Also loved the prest project
thanks a lot for the comment!
Come contribute with us \o/, we need more people helping the project evolve