Strings
A string is an immutable slice of bytes, with literals living in read-only
memory:
import "std/io"
func main() !int { greeting := "hello, world\n" try io.Print(greeting)
// .len is bytes, not characters if greeting.len != 13 { return 1 } return 0}Indexing a string gives you bytes. Decoding UTF-8 is a library’s job, not the
type’s — std/unicode is that library:
import "std/io"import "std/unicode"
func main() !int { word := "ação" try io.Printf("{} bytes, {} characters\n", word.len, unicode.Count(word))
// Walking it by character rather than by byte. Stepping one byte at a // time lands in the middle of one, and the terminal then draws something // that is not what is in the file. mut at u64 = 0 mut columns u64 = 0 for at < word.len { r := unicode.Decode(word, at) orelse break columns += unicode.Width(r.Code) at += r.Bytes } try io.Printf("{} columns\n", columns) return 0}Width is what decides whether a program that draws a screen lines up: a
combining mark takes no columns and a CJK character takes two.
== and != compare content, so a string behaves the way you would expect and
switch over one works:
func kind(method string) int { if method == "GET" { return 1 } return 0}
func main() int { return kind("GET") + 41}Joining two strings makes a third, and the third has to live somewhere. In
Sword nothing allocates without being handed an allocator, so there is no +
between strings at run time; there is strings.Concat, which takes one:
import "std/io"import "std/mem"import "std/strings"
const Greeting = "hello, " // constants join at compile time with +const Banner = Greeting + "world"
func main() !int { name := "ada"
// The C allocator, for a string that lives as long as you like. mut sys := mem.NewSystem() line := try strings.Concat(&sys, Greeting, name, "!\n") try io.Print(line)
// Or space on the stack, gone when the function returns and never freed. mut room := [64]u8{} mut arena := mem.NewArena(room[..]) fields := [3]string{"a", "b", "c"} csv := try strings.Join(&arena, fields[..], ",") try io.Printf("{} {}\n", Banner, csv) return 0}The allocator is not ceremony. It is what lets you read a signature and know a
function cannot touch the heap, and it is what lets a server answer a request
out of an arena that is reset, all at once, when the request is done. Printing
needs none of this: io.Printf writes straight out, and fmt.Sprintf takes an
allocator for the same reason Concat does.