There is a specific frustration that comes up when Go developers look at Rust. The syntax, the expressiveness, the sum types and exhaustive pattern matching, the way enum and match together nearly eliminate entire categories of bugs. Not the borrow checker. Not lifetimes. Not the ownership model. Those are the parts that people describe as “fighting the compiler.” The expressive syntax is the part they wish they could have in their language of choice.
Lisette is a direct response to that frustration. It pairs Rust-inspired syntax with the Go runtime: garbage collection, goroutines, channels, and the rest of the concurrency machinery that makes Go productive for networked services. The borrow checker is absent by design. Memory management is handled by the GC, the same way Go handles it.
What “Rust Syntax” Actually Means Here
When Lisette says it uses Rust syntax, it means the parts of Rust that make code more expressive and less error-prone at the logic level, without the parts that make Rust uniquely safe at the memory level.
That includes algebraic data types with enum, exhaustive pattern matching with match, impl blocks for methods on types, traits for polymorphism, and let bindings with explicit mutability via mut. It includes struct syntax, type inference, and generics. It does not include lifetimes, the borrow checker, or unsafe blocks, because those features only make sense in a context where the compiler is tracking ownership and memory manually.
This is not a shallow imitation. The expressive parts of Rust’s syntax solve real problems. Rust’s enum is a proper sum type, not a C-style tagged union. When combined with match, you get exhaustive case analysis that the compiler enforces. Go has no equivalent. You model optional values with pointer-or-nil, discriminated unions with interface values and type assertions, and errors with the (T, error) return convention. These patterns work, but they require discipline and convention rather than type-system enforcement.
A Lisette function working with an optional database result might look something like this:
enum QueryResult {
Found(User),
NotFound,
Error(String),
}
fn handle_query(result: QueryResult) {
match result {
QueryResult::Found(user) => println!("Got user: {}", user.name),
QueryResult::NotFound => println!("No such user"),
QueryResult::Error(msg) => println!("Query failed: {}", msg),
}
}
The compiler rejects a match that omits any variant. That guarantee is worth a lot, and it has nothing to do with memory safety. It is purely about making illegal states unrepresentable and ensuring all cases are handled. Go’s type system cannot express this.
What the Go Runtime Provides
The Go runtime is not just a garbage collector. It is a carefully engineered piece of infrastructure that includes a cooperative, preemptible scheduler for goroutines, a work-stealing scheduler across OS threads, a low-latency GC tuned for server workloads, and the channel primitives that make CSP-style concurrency natural to write.
Go’s goroutines are extremely cheap. The Go team’s own documentation suggests starting costs on the order of a few kilobytes of stack space, with stacks that grow dynamically. This lets you write servers that spawn a goroutine per request without worrying about thread pool exhaustion. The runtime manages multiplexing thousands of goroutines onto a much smaller pool of OS threads.
The GC in modern Go (1.21 and beyond) targets sub-millisecond pause times for most workloads. It uses a tri-color concurrent mark-and-sweep algorithm with short stop-the-world phases, and it has been continuously tuned since Go 1.5 when it was rewritten to be concurrent. The Go GC guide gives a good picture of the knobs available and the tradeoffs involved.
By compiling to or running on this runtime, Lisette gets all of that for free. Goroutine scheduling, channel communication, memory management, and the ecosystem of profiling and observability tools that Go developers take for granted are all available without Lisette having to implement them from scratch.
The Borrow Checker Tradeoff
Stripping out the borrow checker is not a minor decision. It is the core architectural choice that defines what Lisette is.
Rust’s ownership and borrowing system is what allows it to guarantee memory safety without a garbage collector. No use-after-free, no data races, no double frees. These guarantees are checked at compile time, which means they have zero runtime cost. They are also the source of most of Rust’s notorious learning curve and friction. Writing concurrent Rust code means reasoning explicitly about which thread owns what data, which references are valid for how long, and how to share state safely across thread boundaries using Arc, Mutex, or channels.
Go trades those compile-time guarantees for runtime safety via GC and a data race detector. You can have a data race in Go. The race detector (go test -race) finds them at runtime, not compile time. You pay for safety dynamically rather than statically. For most server-side workloads, this is an entirely reasonable tradeoff. The GC pause is rarely the bottleneck. Networking and I/O dominate.
Listette makes the same tradeoff Go does, which means its safety story is Go’s safety story. This is appropriate and honest. A language using Go’s runtime and claiming Rust-level memory safety would be misleading. What Lisette claims instead is Go-level safety with more expressive, type-safe code at the application logic level.
How This Compares to Other Language Experiments
Listette occupies a specific point in a broader design space that has attracted multiple experiments.
Gleam is the closest parallel in spirit. It uses an ML-influenced syntax inspired heavily by Rust, including Result and Option types, pattern matching, and a module system. It runs on the BEAM virtual machine, which is the Erlang runtime. The pitch is almost identical: expressive type-safe syntax, battle-tested runtime for concurrent and distributed systems. Gleam compiles to both Erlang and JavaScript and has seen meaningful adoption, which suggests the niche is real.
Vale takes a different approach. It is trying to provide memory safety without GC and without Rust’s borrow checker, using a technique called “generational references” that adds a small runtime check to pointer dereferences. Vale aims for Rust-like performance characteristics with less cognitive overhead.
Carbon is Google’s experiment in providing a C++ successor with modern syntax. It interoperates bidirectionally with C++ rather than targeting a different runtime entirely.
None of these are doing exactly what Lisette does. The Rust-syntax-on-Go-runtime pairing is a specific bet: that Go’s runtime is good enough for most use cases, that Go’s own syntax is the main thing holding it back, and that Rust’s syntax (minus the ownership system) is the best available alternative.
The Transpiler Question
One architectural question with projects like this is whether they compile to the host language as an intermediate representation or implement their own backend targeting the host runtime directly.
Compiling to Go source code has real advantages early in a language’s life. You inherit Go’s tooling, its module system, its IDE support via the Go language server, and its compiler optimizations. The generated code is readable and debuggable. The tradeoff is that you are constrained by what Go’s type system can express, and you may hit cases where the source and target type systems don’t map cleanly.
Generating Go code from Rust-like syntax means decisions like how Lisette’s traits map to Go interfaces. Go interfaces are structural (satisfied implicitly by implementing the required methods), while Rust traits are nominal (types explicitly implement traits). These are semantically different, and a compiler that bridges them has to decide which semantics the user gets when there is a conflict.
What the Project Is Actually For
The most honest read of Lisette is that it is for developers who want to write networked Go-style services but find Go’s type system limiting, specifically the absence of algebraic data types and exhaustive pattern matching. It is not trying to replace Rust for systems programming, embedded development, or performance-critical applications where GC is unacceptable. It is not trying to replace Go for developers who are happy with Go.
It targets the overlap: people who see Rust’s expressiveness and want it, but who don’t need or want Rust’s memory model. That is a real group of developers. The ergonomics of writing a REST API in a language with proper sum types and no nil pointer exceptions are genuinely better than Go’s current state of affairs.
Whether Lisette becomes the language that captures that market, or whether it serves primarily as a proof of concept that influences how future versions of Go approach the type system, is an open question. Go 1.18 added generics after years of resistance. The pressure from Rust and TypeScript for more expressive type systems is real and documented. Languages like Lisette are part of that ongoing conversation, whether they achieve mainstream adoption or not.
The experiment is worth watching. The pitch is coherent, the tradeoffs are honest, and the niche it targets has genuine demand.