Literals and escapes
A String is a piece of text, written with double quotes. Single quotes are not strings in Threadon. The basic rules live on the Basics page; this page covers what you can do with a string.
Five escapes work inside a string:
| Escape | Meaning |
|---|---|
\n | newline |
\t | tab |
\" | a literal double quote |
\\ | a literal backslash |
\{ | a literal {; only needed inside f"..."/t"..." (and \} for a literal }) |
def main() -> Int32
print("say \"hi\"")
print("back\\slash")
return 0
Joining and converting
Join strings with +. Both sides must be String — "answer: " + 42 is a compile error. Convert with the cast form String(...) first, or use an interpolated string (below):
def main() -> Int32
name: String = "John"
print("Hello " + name)
print("the number is " + String(42))
return 0
Indexing
s[i] reads the character at position i, counting from 0, and returns it as a one-character String. The index must be an integer.
def main() -> Int32
s: String = "hello"
print(s[0])
print(s[1])
i: Int32 = 4
print(s[i])
return 0
Indexing the character you get back as a normal one-character string: it compares equal to the matching literal, can be printed, and can be concatenated.
def main() -> Int32
s: String = "hi"
print(s[0] == "h")
return 0
Bounds are checked only under --debug. An index past the end — or a negative index — is a runtime error in debug mode (String index out of bounds). Without --debug it's undefined behavior, just like the equivalent list read. See Debug.
Characters are read-only. s[0] = "J" is a compile error (Cannot assign to a character of a String). There is no way to edit a string in place; build a new one instead.
Slicing
s[start:end] copies the characters from start up to (but not including) end. Both bounds are optional and must be integers.
| Slice | Result on "hello" | Meaning |
|---|---|---|
s[:] | hello | whole string |
s[:2] | he | first two characters |
s[2:] | llo | everything from index 2 on |
s[1:4] | ell | three characters from position 1 |
s[0:0] | | empty slice |
def main() -> Int32
s: String = "hello"
print(s[1:4])
print(s[:2])
print(s[2:])
print(s[:])
return 0
Bounds are clamped to the string. A negative bound counts from the end, then the result is clamped to [0, len]. A slice where the start is past the end — or the range is empty — gives an empty string. No bounds error here, ever; slicing is always safe.
def main() -> Int32
s: String = "hello"
print(s[-3:-1]) # last three up to last one
print(s[-100:100]) # clamped to the whole string
print(s[3:1]) # start past end: empty
return 0
There is no step argument. s[::2] is rejected (Slice step is not supported).
Comparing strings by content
All six comparison operators work on strings and compare contents, not pointers: ==, !=, and the ordering operators <, <=, >, >= compare lexicographically (byte by byte).
def main() -> Int32
s: String = "hello"
print(s[1:4] == "ell")
print(s < "jello")
print("abc" < "abd")
print("hi" != "hi")
return 0
Both sides must still be String: "1" < "2" is a True textual comparison, while "1" < 2 is a compile error.
Interpolated strings: f"..." and t"..."
An interpolated string embeds expressions between braces. The expression is normal Threadon — variables, arithmetic, even other calls — and the result is converted to text. There are two kinds, and the difference matters.
f"..." — eager
The expression is evaluated once, at the point the string is built. Later changes to the variables do not show up.
def main() -> Int32
x: Int32 = 3
a: String = f"x is {x} and half is {x / 2}"
x = 99
print(a)
return 0
t"..." — lazy template
Captures the variables and renders them only when the string is read (printed, concatenated, compared, indexed…). The captured variable's value at read time is what appears.
def main() -> Int32
x: Int32 = 3
a: String = t"x is {x}"
x = 99
print(a)
return 0
Two escapes exist to keep braces literal inside an interpolated string: \{ and \}.
def main() -> Int32
print(f"a set: \{1, 2\}")
return 0
Use f"..." when you want a plain string at a point in time; use t"..." when you want a live view of variables (for example, a template read out repeatedly). The example 15_string_ops shows plain string features.
What's missing
- No
len.len(s)is a compile error —lenacceptsListandDictonly. See Builtins. - No iteration.
for c in sdoesn't compile. Walk a string with an index and awhileloop (or a fixedrange) instead. - No searching, replacing, splitting, trimming, or case conversion. The standard library has no string methods.
- No
chartype. Indexing returns a one-characterString;chr(...)builds one. - No formatting control. There's no way to pad, round, or align; floats always print with six decimals.
- String slices and indexes can't be assigned to. There is no in-place editing.
The full list of missing things is on the Limits page.