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
// 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
// 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
implacross 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.