Calling Rust code from Go - the Gambiarra way
Tags: go, rust, showdev, programming
Published: 2023-04-12 04:27:54


![Ferrys gopher](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/17vj4f67tx2o52eitcjs.jpg)



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"](https://www.urbandictionary.com/define.php?term=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](https://words.filippo.io/rustgo/)) 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](https://github.com/Grubba27/go-call-rust). Without further ado, to make this happen the only thing you will need to add to your rust code is [clap](https://github.com/clap-rs/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 happing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imf8klx785igi81hemh0.png)


_Visual model of what is happening._

The steps you will need to follow are:

1. 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.
2. Compile your rust to an executable in the host machine, which can be done before starting the main app.
3. 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](https://github.com/Grubba27/go-call-rust):

Our [Rust code](https://github.com/Grubba27/go-call-rust/blob/main/rust-code/src/main.rs) is below:

```rust
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](https://docs.rs/clap/latest/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](https://github.com/Grubba27/go-call-rust/blob/main/main.go) we will call this binary like this:

```go
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.