A str is immutable UTF-8 text. len(s), s[i] and slices count bytes,
not characters; for c in s and s.chars() walk characters. Methods never change
a string; they return a new one.
fn main(): let s = "hello, brine" let name = "brine" print(s.upper(), len(s), s[7:], s.find("brine")) print(f"{name} has {len(name)} letters, pi is {3.14159:.2f}")HELLO, BRINE 12 brine Some(7)brine has 5 letters, pi is 3.14String literals
Section titled “String literals”Strings use double quotes (single quotes work too). These escapes are the only ones:
| Escape | Meaning |
|---|---|
\n, \t, \r, \0 |
newline, tab, carriage return, NUL |
\\, \", \' |
backslash, quotes |
\{, \} |
literal braces |
\u{XXXX} |
a Unicode code point in hex: "\u{e9}" is "é" |
Anything else, such as \x41, is error E0101.
Bytes and characters
Section titled “Bytes and characters”fn main(): let s = "héllo" print(len(s), len(s.chars())) print(s[0], s[1:3], s[3:]) print(s.chars()) print(s.bytes()) for c in s: print(c, end=" ") print()6 5h é llo["h", "é", "l", "l", "o"][104, 195, 169, 108, 108, 111]h é l l olen(s)is the number of bytes.étakes two bytes in UTF-8, so"héllo"has length 6.s[i]is a one-bytestr. For ASCII text that’s the character ati. Inside a multi-byte character it’s a single byte, which isn’t valid text on its own, so for non-ASCII text uses.chars()orfor c in s.s[a:b]is the bytes fromaup tob. Indexes outside the string anda > bpanic. There are no negative indexes: writes[len(s) - 1].s.chars()is a[str]of characters;s.bytes()is an[int]of byte values.
Why bytes? Byte indexing is constant time and is what parsers and file formats need. Python’s character indexing costs either memory (fixed-width storage) or time.
Strings can’t be changed in place:
fn main(): var s = "abc" s[0] = "x"error[E0404]: cannot assign to a character of a string --> main.br:3:5 |3 | s[0] = "x" | ^^^^ strings can't be changed in place | = help: build a new string, e.g. `s = s[:i] + "x" + s[i + 1:]` = run `brine explain E0404` for more about this errorerror: could not compile due to 1 errorJoining strings
Section titled “Joining strings”+ joins two strings. += on a var appends, growing the string in place when
nothing else holds it, so building a string in a loop is fast:
fn main(): var out = "" for i in 0..5: out += str(i) out += " " let line = "[" + out.rstrip() + "]" print(line, "-".join(["a", "b", "c"]), "ab".repeat(3))[0 1 2 3 4] a-b-c ababab+ needs two strings. "n = " + 5 is error E0305: convert with str(5) or use
an f-string. "ab" * 3 is an error too; write "ab".repeat(3).
Strings compare with ==, !=, <, <=, > and >= (byte order), and
x in s tests for a substring.
Methods
Section titled “Methods”fn main(): let s = "hello, brine" print(s.find("brine"), s.find("zzz"), s.count("l")) print(s.starts_with("he"), s.ends_with("!"), "ri" in s, s.contains("ri")) print(s.split(", "), "a b c".split(), "a,,b".split(",")) print(s.replace("l", "L"), "-".join(["x", "y"]), "ab".repeat(2)) print("[" + " x ".strip() + "]", "[" + " x ".lstrip() + "]", "[" + " x ".rstrip() + "]") print("straße".upper(), "ÉCOLE".lower()) print("one\ntwo\r\nthree\n".lines())Some(7) None 2true false true true["hello", "brine"] ["a", "b", "c"] ["a", "", "b"]heLLo, brine x-y abab[x] [x ] [ x]STRASSE école["one", "two", "three"]| Method | Result | What it does |
|---|---|---|
s.find(sub) |
Option[int] |
byte index of the first sub, or None |
s.contains(sub), sub in s |
bool |
substring test |
s.count(sub) |
int |
non-overlapping occurrences |
s.starts_with(p), s.ends_with(p) |
bool |
|
s.split(sep) |
[str] |
split on every sep; keeps empty parts |
s.split() |
[str] |
split on runs of whitespace; no empty parts |
sep.join(xs) |
str |
join a [str] with sep between |
s.replace(old, new) |
str |
replace every old |
s.repeat(n) |
str |
n copies |
s.strip(), s.lstrip(), s.rstrip() |
str |
remove whitespace from both ends, the start, the end |
s.upper(), s.lower() |
str |
Unicode case conversion |
s.lines() |
[str] |
split on \n or \r\n; no trailing empty line |
s.chars() |
[str] |
each character |
s.bytes() |
[int] |
each byte (0 to 255) |
s.is_digit() |
bool |
non-empty and only 0-9 |
s.is_alpha(), s.is_alnum() |
bool |
Unicode letters (and digits) |
s.is_space() |
bool |
Unicode whitespace |
len(s) |
int |
length in bytes |
find,split(sep),replaceandcountpanic when the pattern is empty (split() separator is empty).find("")isSome(0).strip()and its two variants take no arguments; they remove whitespace only.- Case conversion and
is_alpha/is_alnum/is_spacefollow Unicode, as in Python:"straße".upper()is"STRASSE".is_digitaccepts only0-9, the digitsint()reads, so"٣".is_digit()isfalse.
Characters and code points
Section titled “Characters and code points”fn main(): print(ord("a"), ord("é"), chr(97), chr(0x1F600)) print("42".is_digit(), "-4".is_digit(), "".is_digit(), "é".is_alpha(), " \t".is_space())97 233 a 😀true false false true trueord(c) needs exactly one character and panics otherwise
(ord() needs exactly one character, got a string of length 2). chr(n) panics
for negative numbers and other values that aren’t Unicode scalar values.
f-strings
Section titled “f-strings”An f before the quote makes an f-string: each {expr} is replaced by the value
of the expression, printed the way print shows it. {{ and }} are literal
braces.
fn main(): let name = "brine" let xs = [1, 2] let n = 42 print(f"{name.upper()} {n + 1} {xs} {n > 40} {{braces}}")BRINE 43 [1, 2] true {braces}An expression inside an f-string can’t contain a string literal (f"{"a"}" is
an error); put the string in a variable first.
Format specs
Section titled “Format specs”Add :spec after the expression to format a number or pad a value. The spec is
[<|>][0][width][.precision][kind]:
fn main(): let pi = 3.14159265 let n = 42 let name = "brine" print(f"[{pi:.2f}] [{pi:.0f}] [{pi:e}] [{pi:.3e}] [{pi:.2}]") print(f"[{n:5}] [{n:<5}] [{n:>5}] [{n:05}] [{-n:05}]") print(f"[{n:x}] [{n:b}] [{255:08b}] [{n:.1f}]") print(f"[{name:8}] [{name:<8}] [{pi:10.3f}] [{pi:<10.3f}] [{pi:08.2f}]")[3.14] [3] [3.141593e+00] [3.142e+00] [3.14][ 42] [42 ] [ 42] [00042] [-0042][2a] [101010] [11111111] [42.0][ brine] [brine ] [ 3.142] [3.142 ] [00003.14]| Spec | Meaning | Example |
|---|---|---|
.Nf |
fixed point, N decimals |
{pi:.2f} → 3.14 |
.N |
the same as .Nf |
{pi:.2} → 3.14 |
e, .Ne |
scientific (6 decimals by default) | {pi:.3e} → 3.142e+00 |
x |
hexadecimal, integers only | {255:x} → ff |
b |
binary, integers only | {5:b} → 101 |
N |
minimum width N, right-aligned |
{n:5} → 42 |
<N |
left-align in N columns |
{n:<5} → 42 |
>N |
right-align (the default) | {n:>5} → 42 |
0N |
pad with zeros after the sign | {n:05} → 00042 |
- Every value is right-aligned by default, strings too (Python left-aligns
strings). Use
<for left-aligned columns of text. f,eand.Nneed a number; an integer is printed as a float ({42:.1f}is42.0).xandbneed an integer.- There is no centering (
^), no fill character other than0, and no,thousands separator. An unsupported spec is error E0318:
fn main(): let n = 7 print(f"{n:^5}")error[E0318]: invalid format spec `^5` --> main.br:3:14 |3 | print(f"{n:^5}") | ^ in this f-string field | = help: supported: `.2f` (fixed), `.3e` (scientific), `8` (width), `<8` (left-align), `08` (zero-pad), `x` (hex), `b` (binary) = run `brine explain E0318` for more about this errorerror: could not compile due to 1 errorConverting to and from strings
Section titled “Converting to and from strings”str(x) converts any printable value. int(s) and float(s) parse text, and so
do the sized types (i32("7"), u8("200"), f32("1.5")):
fn main(): print(str(42) + "!", str(2.5), str(true), str([1, 2]), str(Some(3))) print(int("42") + 1, int("-7"), int(" 42"), float("2.5"), float("1e3")) print(str(1.0), str(0.1 + 0.2), str(1e20), str(1e-7))42! 2.5 true [1, 2] Some(3)43 -7 42 2.5 1000.01.0 0.30000000000000004 1e+20 1e-07Bad input panics. Text from outside the program (files, arguments) should be
checked first, for example with is_digit():
fn main(): let text = "4x2" print(int(text))panic: invalid integer: "4x2" --> main.br:3:11| Call | Panics when |
|---|---|
int(s) |
s isn’t a decimal integer ("3.5", "0x1f" and "1_000" all panic) or doesn’t fit in an int |
u8(s), i32(s), … |
the same, or the number is out of range for the type |
float(s) |
s isn’t a number; "inf" and "nan" are accepted |
ord(c) |
c isn’t exactly one character |
chr(n) |
n isn’t a valid code point |
Surrounding whitespace is ignored when parsing (int(" 42\n") is 42).
Floats print in the shortest form that reads back as the same value, always with a
decimal point or exponent: 1.0, 0.30000000000000004, 1e+20.
- Strings are immutable UTF-8. Methods return new strings.
len,s[i]and slices count bytes;for c in sands.chars()count characters.- No negative indexes. Out-of-range indexes and slices panic.
+joins strings only; convert other values withstr()or an f-string.int()andfloat()panic on bad input.
Related: arrays, built-in functions, values and types, error handling.