Skip to content

Types ​

Primitives ​

TypeMeaning
i8 i16 i32 i64signed integers
u8 u16 u32 u64unsigned integers
f32 f64IEEE floats
booltrue / false
charone Unicode scalar value
stringimmutable UTF-8 text, value semantics
OsStringbytes that need not be UTF-8, a name the operating system gave, value semantics
()unit; void is its spelling in a return position
neverthe type of an expression that does not return (panic, return, break)

All primitives except string are Copy. string is cheap-clone: the compiler may copy it implicitly, because a copy is one retain.

The bootstrap subset (Core v0) implements i32 i64 u8 f64 bool char string () never.

Literals ​

LiteralExamplesType
Integer42, 1_000_000, 0xFF, 0b1010i64 unless the context requires another integer type
Float3.14, 1e-9, 2.5e3f64 unless the context requires f32
String"text", escapes \n \t \r \\ \" \0 \u{1F600}string
Template`x = ${expr}`string
Char'a', '\n', '\u{41}'char
Booleantrue, falsebool
Unit()()

There is no null literal: absence of a value is None. An integer literal is read as 64 bits and checked against the type it is written at, so a value that does not fit is refused rather than truncated.

Arithmetic ​

Operands of an operator have the same type: there is no implicit widening between integer types, or between integers and floats. i64 + i32 is an error; write a + (b as i64). Integer division truncates toward zero. Overflow panics, and a build with --release wraps instead.

Conversions ​

as converts between numeric types, from char to an integer, and from bool to an integer. Anything else is an error. An integer becomes a char through char.of(n), which answers None for a number that is not a Unicode scalar value:

talor
fn main(): i32 {
    let big: i64 = 300;
    let low = big as u8;                  // 44: the low eight bits
    let code = 'A' as i64;                // 65
    let half = 7 as f64 / 2.0;            // 3.5
    let letter = match char.of(66) { Some(c) => c, None => '?' };
    println(`${low} ${code} ${half} ${letter}`);
    0
}

Composites ​

talor
Array<T>            // growable array, owned
Option<T>           // Some(T) | None
Result<T, E>        // Ok(T) | Err(E)
Box<T>              // owned heap value
shared T            // a counted allocation: `.clone()` retains, `*r` reads, read-only
dyn Trait           // any type implementing Trait, method chosen at run time
Map<K, V>           // hash map, from std
(A, B)              // tuple; fields t.0, t.1
fn(A): R            // function value

T? is sugar for Option<T>, allowed anywhere a type is written. Generic arguments nest without limit: Option<Array<Token>>, Map<string, Array<i64>>.

A tuple type has at most 16 elements. A tuple is laid out as a struct with one field per element.

What copies, what moves ​

Three classes decide everything about assignment and calls:

  • Copy: every primitive except string, tuples of Copy types, and structs or variants whose parts are all Copy. Copied on assignment and call, freely.
  • Plain: Copy, plus string, plus Option, Result, tuples, structs and variants made only of plain types. Copied implicitly where a copy is what the program needs; moved instead when the copy would be the last use.
  • Everything else (Array, Box, shared, Map, fn, and any type containing one): moved. See ownership.

Floats: the methods of f64 ​

f64 has Rust's methods: a root, a power or a logarithm is a method on the value, a limit is a static on the type, and the mathematical constants are in std.math:

talor
let area = PI * r.powi(2);    // PI from std.math, powi a method of f64

The whole list, and what a float answers at its edges, is on Math.

Nominal, not structural ​

Two structs with the same fields are different types. A type alias (type Id = i64;) is a new name, not a new type.

Talor v0.1.0 - Released under the MIT OR Apache-2.0 license.