What the commands mean
The guide's third page - commands as an enum, parsing numbers, functions that fail with an error, matching on text, and why a list's element is copied out.
The last page split each line into words. This one works out which command the words are, or what’s wrong with them.
Commands as values
Section titled “Commands as values”The stash has three commands, which an enum lists. Two of them carry data:
enum Command { Add { item: String, count: i32 } Take { item: String, count: i32 } Show}
fn main() { let add = Command.Add { item: String("stone"), count: 64 } println("{add}") println("{Command.Show}")}Add { item: stone, count: 64 }
ShowA Command is exactly one of its variants, and only the variant it is has fields, so a Show can’t be asked for its item by mistake. match reads the fields back out, which the last page does. {add} shows the variant and its fields, which helps while a program is being built (comparing and printing).
A function that can fail
Section titled “A function that can fail”Working out a command can fail: the line might name no command, or give lots for a count. A function that can fail says what it fails with, after !:
fn half(n: i32) -> i32 ! String { if n % 2 != 0 { fail "{n} is odd" } return n / 2}
fn main() { match half(8) { ok(h) => println("half is {h}"), err(message) => println(message), } match half(3) { ok(h) => println("half is {h}"), err(message) => println(message), }}half is 4 3 is odd
fail returns the error, here a String, instead of a value. Calx has no exceptions: the caller gets either ok with the value or err with the error, and must say what to do with each, right where it calls. Forgetting is an error:
fn half(n: i32) -> i32 ! String { if n % 2 != 0 { fail "{n} is odd" } return n / 2}
fn main() { let h = half(3)error: half can fail, so handle its error here println("{h}")}So an error never slips past unseen. Errors has the other ways to handle one, such as passing it up with try, which the stash uses on the next page.
Working out a command
Section titled “Working out a command”enum Command { Add { item: String, count: i32 } Take { item: String, count: i32 } Show}
// The words in `line`, which spaces separate.fn words(line: String) -> List<String> { var out = List<String>() var start: usize = 0 for i in 0..line.len() { if line.byte_at(i) == 32 { // a space if i > start { out.push(line[start..i]) } start = i + 1 } } if start < line.len() { out.push(line[start..]) } return out}
// The command `line` gives, or what's wrong with it.fn parse(line: String) -> Command ! String { let w = words(line) if w.len() == 1 and w[0] == "show" { return Command.Show } if w.len() != 3 { fail "write `add ITEM COUNT`, `take ITEM COUNT` or `show`" } let count = i32.parse(w[2]) else { fail "{w[2]} isn't a number" } if count < 1 { fail "{w[0]} needs a count of 1 or more" } let item = w[1].copy() return match w[0] { "add" => Command.Add { item, count }, "take" => Command.Take { item, count }, _ => fail "there's no command {w[0]}", }}
fn main() { let lines: List<String> = ["add stone 64", "take dirt 3", "show", "dig diamond 1", "add torch lots", "take stone 0", "add stone"] for line in lines { match parse(line) { ok(command) => println("{command}"), err(message) => println("{line}: {message}"), } }}Add { item: stone, count: 64 }
Take { item: dirt, count: 3 }
Show
dig diamond 1: there's no command dig
add torch lots: lots isn't a number
take stone 0: take needs a count of 1 or more
add stone: write `add ITEM COUNT`, `take ITEM COUNT` or `show`and,orandnotare the logical operators, written as words.andchecks its right side only when the left is true, sow[0]is only read when there’s a word.i32.parse(w[2])gives ani32?: the number, ornoneif the text isn’t one.let count = ... else { }takes the number out, or runs theelse, which must leave the function, here withfail.matchon text picks the arm whose literal is the same text. Any text could come in, so a_arm, which matches anything, is needed for the rest. Each arm gives a value, theCommand, unless it leaves withfail.Command.Add { item, count }is short forCommand.Add { item: item, count: count }.linesis aList<String>, so its literals areStrings, whichparsetakes. Looping over an array of literals,for line in ["show"], would give each as astr.
Why .copy()
Section titled “Why .copy()”w[1].copy() makes a copy of the item’s name. Taking the name itself is an error:
fn main() { let w: List<String> = ["add", "stone", "64"] let item = w[1]error: can't move an element out of w println(item)}Every value in Calx has one owner, which frees it when it’s done, so a program never frees memory by hand and never frees it twice. The list owns its strings. let item = w[1] would move the string out to item, leaving a hole in the list, so it isn’t allowed; a copy gives item a string of its own. Numbers are copied whenever they’re used, so count needed nothing. Ownership has the rules.
Next: the stash itself.