The toolchain is written in Rust, so the debt is acknowledged. But Keal made the opposite memory trade: reference counting with a documented model instead of a borrow checker. You give up compile-time aliasing guarantees; you get a language with no lifetimes to write.
| Concept | Rust | Keal |
|---|---|---|
| Binding | let x = 1; | val x = 1 |
| Mutable | let mut x = 1; | var x = 1 |
| Struct | struct P { x: f64 } | record P(val x: Float) |
| Option | Option<String> | String? |
| Unwrap or default | opt.unwrap_or(d) | opt ?: d |
| Match | match n { 0 => … } | when (n) { 0 -> … } |
| Trait bound | fn f<T: Ord>(x: T) | func f<T: Ord>(x: T) |
| Closure | |x| x * 2 | { x -> x * 2 } |
| Result | Result<T, E> + ? | try { } catch (e) { } |
| Rc / Weak | Rc<T> / Weak<T> | the default / weak var f: T? |
| Threads | std::thread::spawn | sys.spawn({ self, msg -> … }) |
Every heap value is counted, as if everything were Rc. Assignment shares; there is no move semantics to reason about, and nothing to annotate.
A cycle is the one thing counting cannot free, and weak var is Weak<T> with the upgrade done for you: reading gives the target while it lives, null after.
There is no ? operator and no Result: a failure throws, and catch binds the message. Integer overflow panics in release builds too.
Build a staticlib, declare the symbols with extern func, pass the .a to keal build.