What if references can't be stored?
A research language exploring memory safety
without lifetime annotations or garbage collection.
The idea
Most of Rust's complexity comes from allowing references to be stored in structs and returned from functions. Tracking those references requires lifetime annotations that spread through your codebase.
Rask's experiment: What if references are always temporary — they exist only within a block or expression? When you need longer-lived relationships, use handles (opaque identifiers) instead.
The cost
Explicit indirection for graphs. Some .clone() calls for shared data.
The benefit
No lifetime annotations. No borrow checker fights. Simple mental model.
How it works
Block-scoped borrowing
References can't be stored or returned. They're temporary views, valid only within their block. This eliminates lifetime tracking.
// References are temporary
func process(data: Data) {
const name = data.user.name // temporary borrow
// name is gone at end of block
}
Handles for relationships
For graphs and long-lived relationships, use Pool<T> + Handle<T>. Handles are checked at runtime—no lifetimes needed.
// Graphs without lifetimes
struct Entity {
target: Handle<Entity>?
}
const entities = Pool<Entity>.new()
entities[enemy].target = player_handle
Resource types
Files and system resources must be explicitly consumed. The compiler enforces cleanup on all exit paths.
func backup(src: string, dst: string) -> () or Error {
const file = try fs.open(src)
ensure file.close() // runs on all exits
try write(file)
}
No function coloring
I/O operations pause green tasks automatically. No async/await splitting your ecosystem.
func fetch_user(id: u64) -> User or Error {
const resp = try http_get(url) // pauses task
return parse(resp)
}
The tradeoffs
Here's what you give up and what you get:
| You give up | You get |
|---|---|
| Storable references | No lifetime annotations |
| Implicit string sharing | Explicit .clone() when needed |
| Direct pointer traversal | Handles with runtime checks |
For pointer-level optimization, use Rust or C++. I'm betting that most application code can live with these tradeoffs — but that's still being tested.
What I'm going for
I'm exploring whether no-storable-references can cover the 80% of systems code that's really application code — servers, tools, games — with less friction than Rust. The language draws from Rust's ownership, Zig's comptime, Erlang's supervision, and Vale's generational references. Whether it all holds together is still an open question.
Where we are
Rask is in early development (pre-0.1). There's a working compiler (Cranelift backend) — rask run compiles and executes, rask check type-checks. Expect breaking changes, bugs, and design pivots.
This is a hobby project, but feedback and ideas are welcome on GitHub issues.