Solod (So) is a strict subset of Go that translates to regular C.
Highlights:
- Go in, C out. You write regular Go code and get readable C11 as output.
- Zero runtime. No garbage collection, no reference counting, no hidden allocations.
- Rich standard library. Use familiar types and functions ported from Go's stdlib.
- Native C interop. Call C from So and So from C — no CGO, no overhead.
- Go tooling works out of the box. Syntax highlighting, LSP, linting and "go test".
So supports structs, methods, interfaces, slices, maps, multiple returns, and defer. Everything is stack-allocated by default; heap is opt-in through the standard library. To keep things simple, there are no channels, goroutines, closures, or generics.
So is for Go developers who want systems-level control without learning a new language. And for C programmers who like Go's safety, structure, and tooling.
Example • Installation and usage • Language tour • Standard library • Playground • So by example • Testing • Benchmarks • Compatibility • Design principles • FAQ • Roadmap • Contributing
This Go code in a file main.go:
package main
import "solod.dev/so/time"
type Person struct {
Name string
Age int
Nums [3]int
}
func (p *Person) Sleep() int {
p.Age += 1
return p.Age
}
func main() {
p := Person{Name: "Alice", Age: 30}
p.Sleep()
println(p.Name, "is now", p.Age, "years old.")
p.Nums[0] = 42
println("1st lucky number is", p.Nums[0])
year := time.Now().Year()
println("The year is", year)
}Translates to a header file main.h:
#pragma once
#include "so/builtin/builtin.h"
#include "so/time/time.h"
typedef struct main_Person {
so_String Name;
so_int Age;
so_int Nums[3];
} main_Person;
so_int main_Person_Sleep(void* self);Plus an implementation file main.c:
#include "main.h"
so_int main_Person_Sleep(void* self) {
main_Person* p = (main_Person*)self;
p->Age += 1;
return p->Age;
}
int main(void) {
main_Person p = (main_Person){.Name = so_str("Alice"), .Age = 30};
main_Person_Sleep(&p);
so_println("%.*s %s %" PRId64 " %s", p.Name.len, p.Name.ptr, "is now", p.Age, "years old.");
p.Nums[0] = 42;
so_println("%s %" PRId64, "1st lucky number is", p.Nums[0]);
so_int year = time_Time_Year(time_Now());
so_println("%s %" PRId64, "The year is", year);
}Check out more examples in So by example and learn about the supported language features in the language tour.
Install the So command line tool:
go install solod.dev/cmd/so@latest
Create a new Go project and add the Solod dependency to use the So standard library:
go mod init example
go get solod.dev@latest
Write regular Go code, but use So packages instead of the standard Go packages:
package main
import "solod.dev/so/math"
func main() {
ans := math.Sqrt(1764)
println("Hello, world! The answer is", int(ans))
}Transpile to C:
so translate -o generated .
The translated C code will be saved in the generated directory.
You can also transpile to C and compile the code to a binary in one step. This uses the C compiler set by the CC environment variable:
so build -o main .
Or you can transpile, compile, and run without saving the binary:
so run .
All commands work with Go modules, not individual files (so run ., not so run main.go).
Keep in mind that So is new, so it's still a bit rough around the edges.
To learn about So's features and limitations, check out the brief overview of the language.
So provides a growing set of packages similar to Go's stdlib.
Try So online without installing anything. You can run the code or view the translated C output.
If you like learning by doing, try a hands-on introduction to So with annotated example programs.
So doesn't have its own testing framework. Since So code is valid Go code, you can just use go test like you normally would. Plus, your tests can use all Go features because they're never transpiled.
The transpilation logic is covered by the So compiler's own tests.
So truly shines when it comes to C interop, but it's also quite fast on regular Go code — typically on par with or faster than Go.
So generates C11 code that relies on several GCC/Clang extensions:
- Binary literals (
0b1010) in generated code. - Statement expressions (
({...})) in macros. __attribute__((constructor))for package-level initialization.__auto_typefor local type inference in generated code.__typeof__for type inference in generic macros.allocaand VLAs formake()and other dynamic stack allocations.
You can use GCC, Clang, or zig cc to compile the transpiled C code. MSVC is not supported.
Supported operating systems: Linux, macOS, and Windows (core language only).
So is highly opinionated. Simplicity is key. Heap allocations are explicit. Strictly Go syntax.
I have heard these several times, so it's worth answering.
✅ Core language features.
✅ C interop.
✅ Limited generics and maps.
⏳ Core stdlib packages:
✗ bufio ✓ fmt ✓ os ✓ strings
✓ bytes ✓ io ✗ rand ✓ strconv
✗ filepath ✗ maps ✗ slices ✓ time
✗ flag ✓ math ✗ slog ✓ unicode
⬜ Example applications.
⬜ Concurrency.
⬜ More stdlib packages: crypto, http, json, regexp, ...
🤔 Full Windows support.
Bug fixes are welcome. For anything other than bug fixes, please open an issue first to discuss your proposed changes. To prevent feature bloat, it's important to discuss any new features before adding them.
AI-assisted submissions are fine on one condition: you, the human, have read all the code and fully understand what it does. Code reviewed only by another AI will not suffice.
Make sure to add or update tests as needed.
Go stdlib code by the Go Authors.
Transpiler and So stdlib code by Anton Zhiyanov.
Released under the BSD 3-Clause License.