LANGUAGES Signal 252 2 feeds carried it
Rust's unstable explicit_tail_calls feature allows stack-safe tail-call interpreters
Illustration only Photo by Barn Images on Unsplash
Jimmy Ostler benchmarks tail-call interpreters in Rust, using the unstable explicit_tail_calls feature to avoid stack overflow.
Tail-call optimization is critical for interpreters and functional languages to avoid stack overflow. Rust's explicit tail calls let developers write recursive dispatch loops without growing the stack, but the feature is unstable and requires unsafe code. This article shows a practical implementation and benchmarks different dispatch styles.
Written by elseif from the cluster below · every claim links back to a sourceThe three things worth knowing
The article implements a stack machine with five instructions and a register machine in Rust.
Switch dispatch uses a recursive match statement with the `become` keyword to ensure tail-call optimization.
Subroutine dispatch replaces the match with dynamic dispatch via `&dyn Fn()` traits.
THE READ
What the cluster adds up to.
The article by Jimmy Ostler explores tail-call interpretation in Rust, a technique that turns recursion into jumps to avoid stack growth. The author implements two VM dispatch styles, switch dispatch and subroutine threading, and benchmarks them. The key enabler is Rust's unstable `explicit_tail_calls` feature, which allows the `become` keyword to guarantee tail-call optimization.
Switch dispatch is the simplest: a recursive function that matches on each instruction and calls itself with updated stack and instruction pointers. Using `become` ensures the compiler turns these recursive calls into jumps, preventing stack overflow. However, this relies on the unstable feature and uses `static mut` and `unsafe`, which the author admits is not idiomatic Rust.
Subroutine dispatch replaces the match statement with dynamic dispatch, where each instruction is a struct implementing the `Fn()` trait. This allows calling instructions via `&dyn Fn()` without knowing the concrete type. This approach adds indirection but may improve cache behavior. The author also mentions a more traditional register machine to leverage Rust's strengths.
The cost of adopting this technique is the reliance on an unstable feature that may change or be removed. Additionally, the use of `unsafe` and global mutable state is risky and not recommended for production. The technique stops working if the recursion is not a tail call or if the feature is not enabled. The benchmarks provide insight into the trade-offs, but the article does not present definitive results, only the author's measurements.
Written by elseif from the cluster below · checked for specifics the sources never containedTHE CLUSTER