Part 3 of Goose lang and Flisk.sh 101. Stay in ~/src/goose-flisk-101. Run every file with goose file.goose before you build it.
Goose has no heap allocator and no garbage collector. Dynamic values live on data stacks the compiler assigns. Growth bumps a pointer. Scope exit moves that pointer back. You do not write the stack. You do choose the array spelling, because that spelling is the contract.
The array you want in week one
T[>..] is grow-only. References into it stay valid across push. That is the difference from Vec<T>.
total.goose:
fn total(xs: i64[:]) -> i64 {
var t = 0;
for x in xs { t += x; }
return t;
}
fn main() {
var grow: i64[>..] = [];
for i in 5 { grow.push(i * i); }
print(grow);
print(total(grow));
print(total(grow[1..4]));
}
goose total.goose
i64[:] is a slice: a reference plus a count. total can take the whole array or grow[1..4] without copying elements. ^k counts from the end, as in grow[^2..].
let cannot be reassigned. var can. Types usually infer. fn is a free function. xs.total() would mean total(xs) if you prefer the call shape. There is no impl block.
Strings are byte arrays
There is no string type.
| Spelling | Use |
|---|---|
u8[>..] |
builder |
u8[] |
finished text, frozen after construction |
u8[..16] |
small text inside a struct |
u8[:] |
a view, including string literals |
fn main() {
var s: u8[>..] = [];
s.append("hello");
s.push(',');
format(s, " world! ", 6 * 7);
print(s, " [", s.len, " bytes]");
}
format appends. str(...) builds a new string straight into its destination. Slicing does not copy: trim and find in the standard library hand back views.
Two results, not a tuple
There is no tuple type. A trailing bool is the usual "did this work" signal.
fn divide(a: i64, b: i64) -> i64, bool {
if b == 0 { return 0, false; }
return a / b, true;
}
fn main() {
let q, ok = divide(7, 2);
print(q, " ", ok);
}
Three rules that bite on day one
- Arithmetic uses the operands' width.
u8 + u8is an 8-bit add. A narrow type widens implicitly. Narrowing needsasoras!.u32 + i32does not compile until you pick a side. -
%is Euclidean. The result is never negative.-7 % 3is2. - A growable value is not copied by assignment.
var b = aon ani64[>..]binds a reference.var b = copy(a)is the copy, and you should see it.
if and match are expressions:
let kind = if hp > 50 { "healthy" } else { "hurt" };
What to ignore until the official tutorial
Relative references (Node&<u32>?), reusable pools, return err from load, threads, and extern fn are real and they are not 101. The compiler is also young: no namespace privacy, no labeled break, whole-program compilation only. Read section 18 of the tutorial before you design a service around it.
Leave a binary for part 5
goose build -o ./total total.goose
./total
./total prints and exits. It is not an HTTP server. Part 4 builds one in a language Flisk already scaffolds. Part 5 calls this binary from the host and explains why Docker will not see it.
Top comments (0)