Jule cheatsheet

Back to articles

Created on 2025/10/26

The basics

hello.jule
fn main() {
  message := greetMe("world")
  println(message)
}

fn greetMe(name: str): str {
  ret "Hello, " + name + "!"
}

Note that print and println only accept one parameter. So a more accurate version (i.e. à la Go), would be written like this:

accurate_hello.jule
use "std/fmt"

fn main() {
    name := "Billy"
    fmt::Println("Hello, ", name, "!")
}

Variable declaration
let mut msg: str
let mut msg = "Hello, world!"
let mut msg: str = "Hello, world!"

let immutableMessage: str = "Hello, world!"
let immutableMessage = "Hello, world!"

let (mut x, y) = 10, 20
let (mut x, y, mut z) = true, 1, -400
let (x, _, z) = true, 1, -400

Shortcut
mut msg := "Hello, world!"
immutableMessage := "Hello, world!"

mut x, mut y := 10, 20
x, _, z := true, 1, -400

Constants
const Phi = 1.618
const Size: i64 = 1024
const x, y = 1, 2
const (
  Pi = 3.14
  E  = 2.718
)

const (
  Sunday = iota
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
)

If you have multiple constants that are around a same theme, you can use an enum instead.

Basic types

Numbers
num := 3          // int
num := 3.         // float64
num := 3 + 4i     // cmplx128
num := byte('a')  // byte (alias for u8)
num := rune('a')  // rune (alias for i32)

Arrays
// x := [5]int([1, 2, 3, 4, 5])
x := [...]int([1, 2, 3, 4, 5])

// let x: [5]int = [1, 2, 3, 4, 5]
let x: [...]int = [1, 2, 3, 4, 5]

Slices
// let slice: []int = [2, 3, 4]
slice := [2, 3, 4]
slice := []byte("Hello")

Enums
enum FileMode {
    Read,
    Write,
    Both,
}

enum MyEnum {
    A: -20,
    B, // -20
    C, // -20
    D: 20,
    E, // 20
    F: 1,
    G, // 1
    H, // 1
}

Enum casts
enum FileMode: u8 {
    Read,
    Write,
    Both,
}

enum MyEnum {
    MyVal: 10,
}

fn main() {
    _ = int(MyEnum.MyVal)
    _ = u8(MyEnum.MyVal)
}

Enum matching
enum Foo {
    A,
    B,
    C,
}

fn main() {
    x := Foo.A
    match x {
    | A:
        println("Foo.A")
    | B:
        println("Foo.B")
    | C:
        println("Foo.C")
    }
}

Flow control

Conditional
if day == "sunday" || day == "saturday" {
  rest()
} else if day == "monday" && isTired() {
  groan()
} else {
  work()
}

Pattern matching
match day {
| "sunday":
    // Cases don't fall through by default
    fall
| "saturday":
    rest()
|:
    work()
}

match {
| x > 0:
    println("positive")
| x < 0:
    println("negative")
|:
    println("zero")
}

For loop
use "std/fmt"

mut count := 0
for count <= 10; count++ {
  fmt::Println("My counter is at", count)
}

For-range loop
entries := ["Jack","John","Jones"]
for i, val in entries {
  fmt::Printf("At position {}, the character {} is present\n", i, val)
}

While loop
mut n := 0
x := 42
for n != x {
    n = guess()
}

Functions

Lambdas
myfunc := fn(): bool {
  ret x > 10000
}

Multiple return types
fn getMessage(): (a: str, b: str) {
  ret "Hello", "World"
}

a, b := getMessage()

Named return values
fn split(sum: int): (x: int, y: int) {
  x = sum * 4 / 9
  y = sum - x
  ret
}

Error handling
// Note the `!` after the function signature
fn getFirstElement(slice: []int)!: int {
    if len(slice) == 0 {
        error("slice is empty")
    }
    ret slice[0]
}

fn main() {
    mut x := [1, 2, 3]
    mut first := getFirstElement(x)!
    x = []
    first = getFirstElement(x) else { use -1 }
    println(first) // -1
}

Concurrency

Threads
fn main() {
  // A "channel"
  ch := make(chan str)

  // Start concurrent threads
  co push("Moe", ch)
  co push("Larry", ch)
  co push("Curly", ch)

  // Read 3 results
  // Since our threads are concurrent,
  // the order isn't guaranteed!
  fmt::Printf("{} {} {}\n", <-ch, <-ch, <-ch)
}

fn push(name: str, ch: chan str) {
  msg := "Hey, " + name + "..."
  ch <- msg
}

Buffered channels
ch := make(chan int, 2)
ch <- 1
ch <- 2
ch <- 3
// all threads are asleep - deadlock!

Structs

Defining
struct Vertex {
    X: int
    Y: int
}

fn main() {
  mut v := Vertex{1, 2}
  v.X = 4
  fmt::Println(v.X, " ", v.Y)
}

Struct literals
v := Vertex{X: 1, Y: 2}
// Field names can be omitted
v := Vertex{1, 2}
// Y is implicit
v := Vertex{X: 1}

Reference literals
mut v := &Vertex{1, 2}
v.X = 2
println(*v) // {2, 2}

Useful when you want to create a struct that is guaranteed to be unique (e.g. a a pseudo-random number generator). Think of it like a unique_ptr in C++, or a ref object in Nim.

Methods

Receivers
struct Vertex {
  X: f64
  Y: f64
}

impl Vertex {
    fn Abs(*self): f64 {
        ret math::Sqrt(self.X * self.X + self.Y * self.Y)
    }
}

fn main() {
    v := Vertex{1, 2}
    println(v.Abs())
}

Mutation
// Multiple impl blocks for a same struct are allowed
impl Vertex {
    fn Scale(mut *self, f: f64) {
        self.X *= f
        self.Y *= f
    }
}

fn main() {
    mut v := Vertex{6, 12}
    v.Scale(0.5) // `v` is updated
    println(v)
}

Traits
trait Foo {
    fn foo(*self)
}

trait Bar {
    fn bar(*self)
}

trait FooBar {
    Foo
    Bar
}

struct Baz {}

impl FooBar for Baz {
    fn foo(*self) { println("foo") }
    fn bar(*self) { println("bar") }
}

fn main() {
    let a: FooBar = Baz{}
    a.foo()
    a.bar()
    let b: Foo = a
    b.foo()
    let c: Bar = a
    c.bar()
}

Comptime

Comptime matching
fn printKind[T](value: T) {
  const match type T {
  | *int:
    println("int pointer")
  | &int:
    println("int reference")
  | u32:
    println("u32")
  | i32:
    println("i32")
  | u8:
    println("u8")
  | cmplx128:
    println("cmplx128")
  | cmplx64:
    println("cmplx64")
  | []int:
    println("slice of ints")
  | [5]int:
    println("array of 5 ints")
  |:
        // cause a comptime error
    panic("unexpected type")
  }
}

fn main() {
  let x: [5]int = [1, 2, 3, 4, 5]
  printKind(x) // array of 5 ints
  printKind(3+4i) // cmplx128
  slice := [2, 3, 4] // slice of ints
  printKind(slice)
}

References