Structs and Methods
Structs
A struct is plain data. Fields end with ;, and a field may have a default.
#![allow(unused)]
fn main() {
struct Enemy {
hp: int = 10;
speed: f32 = 2.0;
alive: bool = true;
}
let e = Enemy { speed: 3.5 }; // hp and alive use their defaults
}
Fixed-size array fields have no default, so write them in every literal:
#![allow(unused)]
fn main() {
struct State {
n: int;
ys: [3]int;
}
let s = State { n: 0, ys: [0, 0, 0] };
}
Methods
Methods live inside the struct body. The receiver is the first parameter, named this.
#![allow(unused)]
fn main() {
struct Vec2 {
x: f32;
y: f32;
// by value: small structs are copied
fn length(this: Vec2): f32 {
return sqrt(this.x * this.x + this.y * this.y);
}
// by pointer: changes the caller's value
fn scale(this: &Vec2, s: f32) {
this.x *= s;
this.y *= s;
}
}
let v = Vec2 { x: 3, y: 4 };
let len = v.length();
v.scale(0.5); // `&v` is taken for you
}
There is no fn Vec2.name(...) syntax outside the body.
. follows pointers automatically, and works on any value, not just variables: f().x, (a + b).x, and xs[a..b].len are all fine. A temporary like f().x can be read but not assigned.
Operators
Operators are methods named operator+, operator-, operator*, and so on:
#![allow(unused)]
fn main() {
struct Vec2 {
x: f32;
y: f32;
fn operator+(this: Vec2, o: Vec2): Vec2 {
return Vec2 { x: this.x + o.x, y: this.y + o.y };
}
fn operator*(this: Vec2, s: f32): Vec2 {
return Vec2 { x: this.x * s, y: this.y * s };
}
}
let next = pos + vel * dt;
}
Embedding with use
use on a field embeds it: the inner struct’s fields can be read straight through the outer one. This is composition, not inheritance.
#![allow(unused)]
fn main() {
struct Transform {
use position: Vec2;
use scale: Scale2;
}
let t = Transform {
position: Vec2 { x: 10, y: 10 },
scale: Scale2 { width: 1, height: 1 },
};
printLn(`{t.x} {t.width}`); // same as t.position.x, t.scale.width
}
Only fields are promoted, not methods: write t.position.length(). Two embedded structs with a field of the same name is a compile error.
A use statement brings a value’s fields into the current block as names. They are places, not copies, so assigning one writes the field:
#![allow(unused)]
fn main() {
use t;
width = 2; // writes t.width
}
Printing structs
Mark a struct #[Debug] to put it in a string interpolation hole:
#![allow(unused)]
fn main() {
#[Debug]
struct Vec2 { x: f32; y: f32; }
printLn(`pos = {pos}`);
}
Free functions as methods (UFCS)
x.f(args) calls the free function f(x, args) when x’s type has no method f. The type’s own method always wins.
fn sum(p: P): int { return p.x + p.y; }
fn bump(p: &P, by: int) { p.x += by; }
p.bump(10); // takes &p, like a method with this: &P
let t = p.sum();
fmust be visible by its bare name: declared in this module, or imported withuse m.{ f }. A name reachable only asm.fdoes not count.- The first parameter takes the receiver by value, or as
&T, and the receiver may itself be a&T. - It works on any receiver: structs, enums, primitives (
far.abs()withuse j3.math.{ abs }), slices, strings. - Generic free functions work too:
xs.first()infersTfrom the receiver (see Generics). - Same IR as
f(x, args); the receiver is evaluated once, before the other arguments.
See speculative/lang/ufcs.j3.
extend
extend T { .. } adds methods to a struct declared in the same module, in any file of that module: the main file or a part file (X.j3 plus X/*.j3 saying the same module). It is the same as writing the methods in the struct body, so big types keep their methods next to the code they use.
struct Counter { n: int; }
extend Counter {
fn bump(this: &Counter, by: int) { this.n += by; }
}
extend Box<T> { // a generic struct: parameters named as in `struct Box<T>`
fn first(this: &Box<T>): T { return this.v; }
}
- Methods are public like in a body, so
pub fnis an error. - Errors, each with a fix hint: no such struct, a struct of another module, an enum (write a free fn), a method name already in the body or another block, other type parameters than the struct has.
- An error inside a method points into the file where it is written.
j3c doclists these methods under their struct. - Not available in
.j3sscripts.
See speculative/lang/extend.j3.