Functions, Methods & Structs
Ube models data and behavior cleanly: structs represent plain value types, and functions attach to them using explicit value or mutable receivers.
1. Function Declarations
Functions are declared with fn at top-level. Return types are indicated with -> Type. If a function returns nothing, the return type is void or may be omitted:
fn add(a: i32, b: i32) -> i32 {
return a + b
}
fn log_event(name: string) {
println(f"[EVENT] {name}")
}
fn main() {
let result = add(20, 22)
println(f"Computed: {result}")
log_event("Simulation initialized")
}
2. Struct Definitions & Instantiation
Structs are value types in Ube. They contain named fields and are constructed with TypeName { field: value, ... } syntax:
struct Vector2D {
x: f32,
y: f32
}
struct Player {
name: string,
position: Vector2D,
health: i32
}
fn main() {
let p = Player {
name: "Aria",
position: Vector2D { x: 12.0, y: 34.5 },
health: 100
}
println(f"Player {p.name} at coordinates ({p.position.x}, {p.position.y})")
}
3. Method Receivers (Value vs Mutable)
Similar to Go, methods in Ube are declared with a receiver preceding the function name. Ube distinguishes between:
- Value Receiver
(r Struct): The receiver struct is passed by value (copied). Used for read-only calculations. - Mutable Receiver
(mut r Struct): The struct is mutated directly in place.
struct Rect {
width: i32,
height: i32
}
// 1. Read-only Value Receiver
fn (r Rect) area() -> i32 {
return r.width * r.height
}
// 2. Mutable Receiver (in-place modification)
fn (mut r Rect) scale(factor: i32) {
r.width *= factor
r.height *= factor
}
fn main() {
let mut box = Rect { width: 10, height: 5 }
println(f"Initial area: {box.area()}") // 50
box.scale(2)
println(f"Scaled dimensions: {box.width}x{box.height}") // 20x10
println(f"New area: {box.area()}") // 200
}
box.scale(2), the variable holding the struct must have been declared with let mut. Calling a mutable receiver on an immutable let binding is caught and rejected at compile time!
4. Visibility & Multi-File Organization
By default, items in Ube are private to their file. Use the pub keyword to export structs, enums, functions, and methods across files:
// physics.ube
pub struct RigidBody {
pub mass: f32,
velocity: f32 // Private field
}
pub fn create_body(mass: f32) -> RigidBody {
return RigidBody { mass: mass, velocity: 0.0 }
}
You can import other files directly with import "relative/path.ube":
import "physics.ube"
fn main() {
let body = create_body(50.0)
println(f"Body mass: {body.mass}")
}
5. Why Ube Eliminates Closures & Anonymous Functions
In many languages, closures introduce hidden heap allocations, implicit captures, and tricky lifetime bugs. In game engines and low-latency servers, closures often cause unexpected GC pressure or hidden pointer indirections.
Ube has no anonymous functions or closures. Every function is top-level and explicitly parameterized. This guarantees:
- Predictable Execution: All calls are direct function pointers or static calls.
- Zero Accidental Captures: No variables secretly kept alive on the heap.
- AI Reasoning Clarity: LLMs can reliably trace call graphs and execution paths without hallucinating captured scope variables.