Types and literals

Cuarzo type Generated C type Example
int int64_t: 64 bits on every system 42
i8, i16, i32, i64 fixed-width signed integer 42
uint uint64_t 42
u8, u16, u32, u64 fixed-width unsigned integer 42
float, f32 float 3.14
f64 double 3.14
str char* "text"
bytes pointer to a length and the data b"GET \x00"
char char 'C'
bool int true, false
void void function returns only
enum int Color.RED
enum whose members carry values pointer to a tagged object Shape.CIRCLE(1.0)
struct pointer to a C struct Point(1, 2)
T[] dynamic array [1, 2, 3]
map[K, V] hash map {"a": 1}
func(T, U) R function value func(int n) => n * 2
(T, U) tuple (1, "one")

Number literals

Integers are written in decimal, in hexadecimal after 0x, in octal after 0o, or in binary after 0b; floats have a decimal point, an exponent, or both. A _ can separate digits anywhere between two of them:

int mask = 0xff
int mode = 0o755
int flags = 0b1010_0001
i64 big = 9_000_000_000
f64 tiny = 1.5e-9
f64 avogadro = 6.022e23

An integer literal is an int, 64 bits, so 1 << 40 and 100000 * 100000 are exact; one that does not fit in an int is an error. Written where a narrower type is expected, a literal must fit it: u8 b = 300 and i32 n = 3000000000 are errors, not values that wrap around. int and uint are as wide as i64 and u64 on every system; the narrower types are for binary data and for C, and arithmetic on them works as in C, where a value narrower than 32 bits becomes an i32 first.

Conversions

Numbers convert to each other on their own when nothing is lost: an int can be used where an f64 is expected, and an int where an i32 is, with the value wrapping around if it does not fit, unless the value is a literal, which must fit. A float never becomes an integer on its own, since that would silently drop its fraction:

f64 average = 7.5
int count = average        // error: write int(average)
int rounded = int(round(average))

A conversion is written with the name of a numeric type, as in int(x), i64(x), u8(x), or f64(x):

  • a float becomes an integer by dropping its fraction, toward zero, and a value that does not fit, or NaN, stops the program with a runtime error;
  • an integer becomes a narrower integer by wrapping around, so u8(300) is 44;
  • a char becomes an integer as its byte code, so int('a') is 97.

For bool, compare instead, as in x != 0.

null is the empty reference. It can initialize or be compared with any reference type (str, bytes, arrays, maps, and structs):

str value = null

Strings and characters accept these escapes:

Escape Meaning
\n, \t, \r line feed, tab, carriage return
\a, \b, \f, \v bell, backspace, form feed, vertical tab
\\, \", \' backslash and quotes
\e the escape character, which starts terminal codes such as colors
\xHH the byte with two hexadecimal digits, such as \x1b; not \x00
\u{H...} a Unicode character as UTF-8, such as \u{e9} or \u{1F600}

Any other escape is rejected, and no escape can produce a NUL byte. A char holds one byte: an escape that yields one byte, or one printable ASCII character other than ' and \. UTF-8 text can also be written directly in strings and comments.

void is only valid as a function return type.