Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

rask logo

Safety without the pain.

Rask is a systems programming language that sits between Rust and Go:

  • Rust’s safety guarantees without lifetime annotations
  • Go’s simplicity without garbage collection

Status: Early development with working compiler (Cranelift backend)

Quick Look

func search_file(path: string, pattern: string) -> () or IoError {
    const file = try fs.open(path)
    ensure file.close()

    for line in file.lines() {
        if line.contains(pattern): println(line)
    }
}

No lifetime annotations. No borrow checker fights. No GC pauses.

Core Ideas

  • Value semantics - Everything is a value, no hidden sharing
  • Single ownership - Deterministic cleanup, no GC
  • Scoped borrowing - Temporary access that can’t escape
  • Handles over pointers - Validated indices for graphs and cycles
  • Linear resources - Files and sockets must be explicitly consumed
  • No function coloring - I/O just works, no async/await split

Get Started

Note: Rask is in early development. The documentation is sparse and might be outdated, bugs are warranted.

Design Philosophy

Want to understand the “why” behind Rask’s design choices?

Getting Started

Welcome to Rask! This section will help you get started with the language.

Note: Rask is in early development (pre-0.1). Expect breaking changes.

What You’ll Learn

Status

Rask has a working compiler (Cranelift backend). All five litmus test programs pass type-checking:

  • Grep clone ✓
  • Game loop ✓
  • Text editor ✓
  • HTTP server ✓
  • Embedded sensor ✓

Next Steps

After completing this section, continue to the Language Guide to learn core concepts.

Installation

Note: Rask is in early development (pre-0.1). Expect breaking changes.

Prerequisites

  • Rust toolchain (for building from source)
  • Git
  • A C compiler (cc) — the runtime is compiled from C

Building from Source

git clone https://github.com/rask-lang/rask.git
cd rask/compiler
cargo build --release

The rask binary will be in compiler/target/release/rask.

Verify Installation

./target/release/rask --version

CLI Commands

CommandWhat it does
rask run <file>Compile and execute a .rk program
rask check <file>Type-check without running
rask fmt <file>Format source code
rask lint <file>Check style and idioms

Compiled binaries are written to build/debug/.

Running Examples

The repository includes working examples:

./target/release/rask run ../examples/hello_world.rk

Next Steps

Your First Program

Create a file called hello.rk:

func main() {
    println("Hello, Rask!")
}

Run it:

rask run hello.rk

Output:

Hello, Rask!

What’s Happening?

  • func main() is the program entry point
  • println() is a builtin for printing with newline

Variables

Let’s try variables:

func main() {
    const name = "Rask"
    const year = 2026
    println(format("Hello from {} in {}!", name, year))
}
  • const creates a permanent binding — the name can’t be reassigned, but the value is still mutable
  • let creates a rebindable name (for values you’ll reassign)
  • Types are inferred, but you can write them explicitly: const year: i64 = 2025

Functions

func greet(name: string) {
    println(format("Hello, {}!", name))
}

func main() {
    greet("World")
}

Functions that return values need explicit return:

func add(a: i32, b: i32) -> i32 {
    return a + b
}

func main() {
    const result = add(2, 3)
    println(format("2 + 3 = {}", result))
}

Next: Explore the Guide

Continue to Language Guide →

Learn Rask

Language Guide

Note: The language guide is being written. Rask is in active design — the specifications are the authoritative source for now.

Where to Learn

Examples

Real Rask programs that demonstrate practical patterns.

All example code is in the repository’s examples/ folder.

Available Examples

Running Examples

git clone https://github.com/rask-lang/rask.git
cd rask/compiler
cargo build --release
./target/release/rask run ../examples/grep_clone.rk --help

View all examples on GitHub →

What These Demonstrate

Each example showcases key Rask concepts:

Grep Clone

  • CLI argument parsing
  • File I/O with error handling
  • String operations
  • Resource cleanup with ensure

Game Loop

  • Entity-component system using Pool<T>
  • Handle-based indirection
  • Game state management

Text Editor

  • Command pattern for undo/redo
  • File I/O and resource management
  • State transitions

Grep Clone

A command-line tool for searching files with pattern matching.

Full source: grep_clone.rk

Key Concepts Demonstrated

  • CLI argument parsing
  • File I/O with error handling
  • String operations (split, contains, trim)
  • Resource cleanup with ensure
  • Pattern matching with enums

Highlights

Resource Management

func search_file(path: string, pattern: string) -> () or IoError {
    const file = try fs.open(path)
    ensure file.close()  // Guaranteed cleanup

    for line in file.lines() {
        if line.contains(pattern): println(line)
    }
}

The ensure keyword guarantees file.close() runs even on early returns or errors.

Error Handling

enum GrepError {
    NoPattern,
    NoFiles,
    FileError(string),
}

func parse_args(args: Vec<string>) -> Options or GrepError {
    // Returns Result type, caller must handle errors
}

String Processing

for line in file.lines() {
    if case_insensitive {
        if line.to_lowercase().contains(pattern.to_lowercase()) {
            println(line)
        }
    } else {
        if line.contains(pattern) {
            println(line)
        }
    }
}

Running It

rask grep_clone.rk "pattern" file1.txt file2.txt
rask grep_clone.rk -i "case-insensitive" *.txt

What You’ll Learn

  • How to parse command-line arguments in Rask
  • Error handling patterns with Result types
  • Resource management with ensure
  • String manipulation and iteration

View full source →

Game Loop

An entity-component system demonstrating handle-based indirection.

Full source: game_loop.rk

Key Concepts Demonstrated

  • Entity-component system with Pool<T>
  • Handle-based references (no pointers!)
  • Game state management
  • Frame-based update loop

Highlights

Entity Storage

struct Entity {
    pos: Vec2,
    vel: Vec2,
    health: i32,
    target: Option<Handle<Entity>>,  // Handle, not reference!
}

const entities = Pool.new()
const player = try entities.insert(Entity.new())
const enemy = try entities.insert(Entity.new())

// Enemy targets player using handle
entities[enemy].target = Some(player)

Update Loop

func update(delta: f32) using entities: Pool<Entity> {
    for h in entities {
        entities[h].pos.x += entities[h].vel.x * delta
        entities[h].pos.y += entities[h].vel.y * delta

        // Handle AI, collision, etc.
    }
}

Each entities[h] access is expression-scoped - the borrow ends at the semicolon. This allows mutation between accesses.

Why Handles Work

Unlike references, handles:

  • Can be stored in structs
  • Can form cycles (entity targets another)
  • Are validated at runtime (pool ID + generation)
  • Don’t need lifetime annotations

Running It

rask game_loop.rk

What You’ll Learn

  • How to use Pool<T> for entity systems
  • Handle-based indirection patterns
  • Expression-scoped borrowing for collections
  • Game loop structure in Rask

View full source →

Text Editor

A text editor with undo/redo functionality.

Full source: text_editor.rk

Key Concepts Demonstrated

  • Command pattern for undo/redo
  • File I/O and resource management
  • State transitions
  • Vec usage for history

Highlights

Command Pattern

enum Command {
    Insert(usize, string),
    Delete(usize, usize),
    Replace(usize, usize, string),
}

struct Editor {
    content: string,
    history: Vec<Command>,
    position: usize,
}

Undo/Redo

func undo(editor: Editor) {
    if editor.position > 0 {
        editor.position -= 1
        const cmd = editor.history[editor.position]
        reverse_command(editor, cmd)
    }
}

func redo(editor: Editor) {
    if editor.position < editor.history.len() {
        const cmd = editor.history[editor.position]
        apply_command(editor, cmd)
        editor.position += 1
    }
}

File Operations

func save(editor: Editor, path: string) -> () or IoError {
    const file = try fs.create(path)
    ensure file.close()

    try file.write(editor.content)
}

func load(path: string) -> Editor or IoError {
    const file = try fs.open(path)
    ensure file.close()

    const content = try file.read_to_string()
    return Editor { content, history: Vec.new(), position: 0 }
}

Running It

rask text_editor.rk

What You’ll Learn

  • Command pattern for undo/redo
  • Resource management with files
  • State management in Rask
  • Vec operations for history tracking

View full source →

Reference

Detailed technical documentation for Rask.

For Users vs Implementers

This book provides high-level user-facing documentation. For formal specifications and implementation details, see:

Coming Soon

Once the language stabilizes:

  • Generated API documentation
  • Standard library reference
  • Compiler command-line reference
  • Error code index

Current Status

Rask is in the design phase. The formal specifications are actively being developed and are the canonical source of truth for the language.

For now, refer to:

Formal Specifications

The formal language specifications are maintained in the repository’s specs/ directory. These are detailed technical documents for language implementers and those who want deep understanding.

View Specifications →

Organization

Specs are organized by topic:

  • Types - Type system, generics, traits
  • Memory - Ownership, borrowing, resources
  • Control - Loops, match, comptime
  • Concurrency - Tasks, threads, channels
  • Structure - Modules, packages, builds
  • Stdlib - Standard library APIs

Quick Access

Key specifications:

TopicLink
Ownershipownership.md
Borrowingborrowing.md
Collectionscollections.md
Poolspools.md
Error Typeserror-types.md
Concurrencyasync.md

For Users vs Implementers

  • This Book - User-facing documentation (“How do I use Rask?”)
  • Specs - Formal specifications (“How does Rask work internally?”)

Most Rask users won’t need the specs. If you’re:

  • Building applications → This book is for you
  • Building compilers/tools → Read the specs
  • Curious about internals → Specs provide complete detail

See Also

Playground

Try Rask directly in your browser with our interactive playground!

Open in full screen →

Features

The playground provides:

  • Online editor with syntax highlighting
  • Instant execution - no setup required
  • 🔗 Shareable code snippets via URL
  • 📚 Example programs to explore
  • 🎮 Quick experimentation without installation

How to Use

  1. Write code in the left editor pane
  2. Click “Run” or press Ctrl+Enter to execute
  3. View output in the right pane
  4. Load examples from the dropdown menu
  5. Share your code with the “Share” button

Limitations

The browser-based playground has some limitations compared to local execution:

  • No file I/O - fs module is disabled
  • No networking - net module is disabled
  • No stdin - interactive input not supported
  • Most features work - math, collections, json, pattern matching, etc.

Try These Examples

Click the examples dropdown in the playground to try:

  • Hello World - Basic println and output
  • Collections - Working with Vec, structs, and pattern matching
  • Pattern Matching - Demonstrating match expressions
  • Math Demo - Mathematical operations and calculations

Local Development

For full language features including file I/O and networking:

  1. Install Rask locally
  2. Run the examples
  3. Build real applications

Technical Details

The playground compiles the Rask interpreter to WebAssembly using wasm-pack. Code executes entirely in your browser with no server-side processing.

WASM bundle size: ~200KB gzipped Supported browsers: Chrome, Firefox, Safari (latest versions)

Source Code

The playground is open source:

Feedback

Found a bug or have a suggestion? Open an issue on GitHub!

Contributing

Rask is in active design and development. Contributions welcome!

How to Help

  • Try it out - Run examples, report bugs
  • Review specs - Provide feedback on language design
  • Implement features - Check TODO.md for open tasks
  • Documentation - Improve this book

Getting Started

  1. Read the Design Process to understand Rask’s philosophy
  2. Check out the formal specifications
  3. Explore the CORE_DESIGN.md document
  4. Look at TODO.md for what needs work

Repository

github.com/rask-lang/rask

Ways to Contribute

Bug Reports

Found a bug? Open an issue.

Include:

  • Code that demonstrates the bug
  • Expected behavior
  • Actual behavior
  • Compiler output

Feature Suggestions

Have an idea? Open an issue for discussion.

Consider:

  • Does it align with core principles?
  • What’s the tradeoff?
  • How does it affect the litmus tests?

Code Contributions

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests: cd compiler && cargo test
  5. Submit a pull request

Documentation

Improvements to this book are welcome:

  • Fix typos and clarity issues
  • Add examples
  • Improve explanations
  • Expand placeholder sections

Community Guidelines

  • Be respectful and constructive
  • Focus on technical merit
  • Consider tradeoffs and design constraints
  • Test your changes

Questions?

Open a discussion or issue on GitHub.

Design Process

Rask’s design is guided by clear principles and measured against specific metrics.

Design Principles

  1. Safety Without Annotation - Memory safety without lifetime markers
  2. Value Semantics - No hidden sharing or aliasing
  3. No Storable References - References can’t escape scope
  4. Transparent Costs - Major costs visible in code
  5. Local Analysis Only - No whole-program inference
  6. Resource Types - I/O handles must be consumed
  7. Compiler Knowledge is Visible - IDE shows inferred information

Full details: CORE_DESIGN.md

Validation

Rask is validated against test programs that must work naturally:

  1. HTTP JSON API server ✓ (passes type-checking)
  2. grep clone ✓ (implemented)
  3. Text editor with undo ✓ (implemented)
  4. Game loop with entities ✓ (implemented)
  5. Embedded sensor processor ✓ (passes type-checking)

Litmus test: If Rask is longer/noisier than Go for core loops, fix the design.

Metrics

Design decisions are evaluated using concrete metrics:

  • Clone overhead (% of lines with .clone())
  • Handle access cost (nanoseconds)
  • Compile times (seconds per 1000 LOC)
  • Binary size
  • Memory usage

See METRICS.md for the scoring methodology.

Specs and RFCs

Language features are documented as formal specifications in specs/.

Major changes follow an RFC process:

  1. Open an issue for discussion
  2. Draft a specification
  3. Implement in compiler
  4. Validate against litmus tests
  5. Update metrics
  6. Merge if it improves the design

Tradeoffs

Every design has tradeoffs. Rask makes these intentional choices:

  • More .clone() calls - Better than lifetime annotations (our view)
  • Handle overhead - Better than raw pointers with manual tracking
  • No storable references - Simpler mental model, requires restructuring some patterns
  • Explicit costs - Better than hidden complexity

See CORE_DESIGN.md § Tradeoffs for full discussion.

Contributing to Design

When proposing changes:

  1. Explain the problem - What use case is difficult today?
  2. Show the tradeoff - What does this cost?
  3. Test against litmus tests - Does it make real programs better or worse?
  4. Measure the impact - Update relevant metrics
  5. Consider alternatives - What other approaches exist?

The goal is ergonomics without hidden costs. If a feature hides complexity or breaks transparency, it probably doesn’t belong.

Philosophy

“Safety is a property, not an experience.”

Users shouldn’t think about memory safety—they should just write code. The type system and scope rules make unsafe operations impossible by construction.

“If Rask needs 3+ lines where Go needs 1, question the design.”

Ceremony should be minimal. Explicit costs are good; boilerplate is bad.

“Local analysis only.”

Compilation should scale linearly. No whole-program inference, no escape analysis. Function signatures tell the whole story.

Learn More