The 0.3.0 version bump is mostly earned by structural bounds, a type system feature that’s been in the design notes since before the first alpha. The rest of the release is a mix of things structural bounds made necessary to finish, things that accumulated across the 0.2.x cycle, and a large batch of codegen hardening that should have happened much earlier.
Structural bounds. Trait-bounded generics used to require explicit impl Trait for T declarations. You had to define a trait, implement it for every type you cared about, then bound the generic on that trait. That still works, but now you can write structural bounds directly:
fn broadcast<T: { fn send(msg: String) }>(targets: Vec<T>, msg: String) {
for t in targets { t.send(msg); }
}The type checker verifies that T has the required methods at each call site — no trait declaration, no impl block. (This is purely structural: if it has .send(String), it matches. It doesn’t replace dynamic dispatch or trait objects, and it doesn’t do anything about coherence.)
The feature shipped in two stages: E1 added the scaffold and parsing, E2 added live method-presence checking. BoundsNotSatisfied errors now include a hint naming the specific missing method.
In-process REPL. hew eval now compiles and runs code using the same codegen pipeline as hew run — the compiler and MLIR backend are loaded in-process rather than shelled out to a subprocess. Startup is faster, error messages are richer, and :type handles numeric expressions correctly. (The old subprocess approach gave hew eval a slightly stale view of the stdlib. That’s gone.)
hew eval - reads from stdin, which makes it composable:
echo 'println("hello")' | hew eval -There’s also a timeout now. Long-running REPL expressions no longer wedge the terminal.
Rc<T>. Reference-counted shared pointers with an explicit user-facing surface: Rc::new(v), rc.clone(), rc.get(), Rc::strong_count(&rc). This isn’t garbage collection — you’re still responsible for avoiding cycles — but it lets you express “multiple owners” without restructuring everything into a tree or routing it through an actor. The type checker enforces the actor boundary: sending an Rc<T> in an actor message is a compile error.
Generic lambdas. Lambda expressions can now be generic:
let identity = fn<T>(x: T) -> T { x };
let n = identity(42);
let s = identity("hello");Captured environment variables are monomorphized per concrete instantiation. (This is the env-bound slice — unbounded generic lambdas, where the lambda itself is a generic parameter, come later.)
NetError and fallible connections. try_connect and try_listen return Result<Connection, NetError> instead of panicking on failure:
match net.try_connect("127.0.0.1:8080") {
Ok(conn) => { /* ... */ }
Err(NetError::Refused) => { /* port not open */ }
Err(NetError::Timeout) => { /* slow or unreachable */ }
Err(e) => { /* ... */ }
}connect and listen still exist for scripts where you’d rather crash on network failure.
SMTP. std::net::smtp gets one-shot send helpers:
import std::net::smtp;
smtp.send("smtp://mail.example.com:587", from, to, subject, body);Credentials are read from environment variables. It’s enough to send a notification without building a full handshake loop.
String.chars(). Returns an iterator over Unicode scalar values. Combined with the new iterator operations (take, skip, count, product) and the String and f64 iterator helpers, you can do a reasonable amount of string processing without a manual index counter.
Stdlib surface expansion. A large batch accumulated across the 0.3.0 cycle:
Vec:index_of,first,last,min,maxiter:take,skip,count,productString.split,String.lines,String.join— reimplemented in pure Hew, replacing the FFI wrappersdatetime.try_parse— returnsResult<DateTime, String>instead of panicking on bad inputjson.try_parse,yaml.try_parse,toml.try_parse— typed parse errors instead ofOption<Value>Collection.clone()— deep clone for Vec, HashMap, HashSetRequest.headers()andResponse.headers()— bulk header accessors on both sides of the HTTP surface
Blocking call detection. The type checker now warns when you call a blocking function (file I/O, network, sleep) inside an actor receive fn. The warning names the call and explains why it stalls the scheduler. It’s a warning, not an error — but if you see it, you probably want to move that work to a dedicated worker actor.
hew-observe. Active node switching (change which cluster node you’re watching without restarting the observer) and multi-node status (see the connected/disconnected state of every node at once). The cluster routing table is also surfaced — no more --debug flag to see which nodes know about each other.
hew fmt. hew fmt - formats stdin and writes to stdout, for editor integrations that expect a stdin/stdout formatter. hew fmt --check now batches errors across multiple files instead of stopping at the first.
hew run --timeout. Kills the program after N seconds. Useful for CI jobs where a hang is indistinguishable from a long run.
Codegen hardening. Around 50 fix(codegen): fail close ... commits this cycle. The pattern: codegen was returning nullptr when MLIR op generation failed, and callers assumed it never would. Every null path now either returns an error or asserts — the visible effect is better error messages instead of silent bad codegen or internal null dereferences inside the compiler.
WASM parity. A large batch of WASM fixes to match native runtime behaviour: actor lifecycle, mailbox close handling, ask/channel patterns, reply channel finalization. The WASM and native runtimes now run a shared parity test matrix.
LSP. Cross-file goto-definition now works through the import graph. Open-import references are surfaced. Rename refactoring now has parity with other symbol types.
Cross-target --emit-obj. hew build --emit-obj --target x86_64-unknown-linux-gnu produces an object file for a target you can’t run locally. Linking is still on you, but the object generation works.