Skip to content

Structs

A struct groups related data with explicit field names. The type name is required at construction — Point { x: 1, y: 2 }, never just { x: 1, y: 2 }.

Definition, construction, field access with ., nested structs, arrays as fields, and field mutation.

04-structs-basic.zolo
Playground
// Feature: Structs — named records (typed fields)

// Syntax: `struct Name { field: T, ... }`

// Construction: `Name { field: value, ... }`

// When to use: group related data with clear names; pass an

// "object" without needing a full class.


// Basic definition.

struct User {
  name: str,
  age: int,
  email: str,
}

// Create an instance — the struct name is REQUIRED.

let u = User { name: "Alice", age: 30, email: "[email protected]" }

// Dot access.

print(u.name)  // Alice

print(u.age)  // 30

print(u.email)  // [email protected]


// Nested structs.

struct Address {
  city: str,
  country: str,
}

struct Account {
  user: User,
  address: Address,
}

let acc = Account { user: User { name: "Bob", age: 25, email: "[email protected]" }, address: Address { city: "Curitiba", country: "BR" } }

print(acc.user.name)  // Bob

print(acc.address.city)  // Curitiba


// Struct with array field.

struct Team {
  name: str,
  members: [str],
}

let t = Team { name: "Zolo Core", members: ["alice", "bob", "carol"] }
print(t.name)
for m in t.members {
  print("  - {m}")
}

// Field mutation (a non-immutable struct is a "mutable" instance by default

// inside methods; see 05-structs-with-impl).

struct Counter {
  value: int,
}

let c = Counter { value: 0 }
c.value = c.value + 1
c.value = c.value + 1
print(c.value)  // 2

Methods with impl

To add behaviour to a struct, use an impl block. Functions without self are associated functions (constructors); functions with self are instance methods:

Rectangle::new, area, perimeter, scale; multiple impl blocks for Stack.

05-structs-with-impl.zolo
Playground
// Feature: Structs with `impl` — methods and constructors

// Syntax: `impl Name { fn method(self, ...) -> R { ... } }`

// Associated constructor: `fn new(...) -> Name { ... }` (no `self`).

// When to use: give behavior to the data type, encapsulate

// invariants, implement an OO-style API.


use std::Array

struct Rectangle {
  width: float,
  height: float,
}

impl Rectangle {
  // Associated function (no `self`) — called via `Rectangle::new`.

  fn new(w: float, h: float) -> Rectangle {
    return Rectangle { width: w, height: h }
  }
  // Instance method — first arg is `self`.



  fn area(self) -> float {
    return self.width * self.height
  }

  fn perimeter(self) -> float {
    return 2.0 * (self.width + self.height)
  }

  fn is_square(self) -> bool {
    return self.width == self.height
  }
  // Method that returns a new struct (functional immutability).



  fn scale(self, factor: float) -> Rectangle {
    return Rectangle { width: self.width * factor, height: self.height * factor }
  }
}

let r = Rectangle::new(10.0, 5.0)
print(r.area())  // 50

print(r.perimeter())  // 30

print(r.is_square())  // false


let big = r.scale(2.0)
print(big.area())  // 200

print(big.width)  // 20


// Multiple `impl` blocks — you can split by topic.

struct Stack {
  items: [int],
}

impl Stack {
  fn new() -> Stack {
    return Stack { items: [] }
  }
}

impl Stack {
  fn push_item(self, x: int) {
    self.items.push(x)
  }

  fn pop_item(self) -> int {
    return self.items.pop()
  }

  fn size(self) -> int {
    return self.items.len()
  }
}

let s = Stack::new()
s.push_item(1)
s.push_item(2)
s.push_item(3)
print(s.size())     // 3

print(s.pop_item()) // 3

print(s.size())     // 2

A few details:

  • Associated functions are called as TypeName::fn(args).
  • Methods are called as instance.method(args).
  • You can split an impl across multiple blocks — the compiler merges them.

Challenge

Add a method describe(self) -> str to Rectangle that returns "rectangle {width}x{height}" and print it for two different rectangles.

See also

enespt-br