Literals

A literal is a value written directly in the source. Rux has integer, floating-point, boolean, character and string literals.

LiteralExamplesType without a suffix or prefix
Integer42, 0xFF, 0o17, 0b1010, 200u8int, or the type the context requires
Floating-point3.14, 1e-9, 0.5f32float64
Booleantrue, falsebool
Character'A', '\n', c8'A', c16'Ж'char32, or char8 / char16 from the context
String"text", c16"text", c32"text"char8[..]

Integer literals

IntegerLiteral ::= ( Decimal | '0x' Hex | '0o' Octal | '0b' Binary ) IntegerSuffix?
Decimal        ::= Digit ( '_'? Digit )*

An integer literal is written in decimal, or in another base after a prefix. The prefix letter may be upper or lower case, and so may hexadecimal digits.

let decimal = 255;
let hex = 0xFF;
let octal = 0o377;
let binary = 0b1111_1111;
let grouped = 8_100_000_000;

The first four are the same value, 255; the base is only how the source spells it. An underscore may separate two digits, anywhere in the number, and is ignored. It must stand between digits: 1__0 and 1_ are error: numeric separator '_' must appear between digits.

A minus sign is not part of the literal — it is the negation operator — but a negated literal is range-checked as one value, so -128i8 and -9223372036854775808 are accepted.

Suffixes

A suffix fixes the literal's type:

SuffixTypeSuffixType
iintuuint
i8int8u8uint8
i16int16u16uint16
i32int32u32uint32
i64int64u64uint64
i128int128u128uint128
i256int256u256uint256
i512int512u512uint512
let small = 200u8;
let mask = 0xFFFF_0000u32;
let index = 3u;
let huge = 340282366920938463463374607431768211455u128;

A float suffix (f8 … f512) on a whole number makes a floating-point literal: 1f32 is the float32 value 1.0.

In a hexadecimal literal, a–f are digits, so a trailing f8 or f32 is read as more digits: 0xFFf8 is the integer 65528. A suffix after a hexadecimal number has to start with a letter that is not a hexadecimal digit, as u8 and i32 do.

Type of an unsuffixed integer

An integer literal without a suffix has no fixed type. It takes the type its context requires:

  • the declared type of the binding, field, array element, parameter or return value it initialises;
  • the type of the other operand of an arithmetic, bitwise or comparison operator.

With no such context it is an int. Either way, the value must fit:

let level: uint8 = 200;
let raised = level + 100;   // 100 is a uint8; the sum wraps to 44
let plain = 200 + 100;      // both are int: 300
let tooBig: uint8 = 256;
error: integer literal is out of range for type 'uint8'

let count: uint = 3;
let missing = count == -1;
error: integer literal is out of range for type 'uint'

An integer literal never becomes a float: let ratio: float64 = 1; is error: cannot assign 'int' to 'float64'. Write 1.0.

Floating-point literals

FloatLiteral ::= Decimal '.' Decimal Exponent? FloatSuffix?
               | Decimal Exponent FloatSuffix?
               | Decimal FloatSuffix
Exponent     ::= ( 'e' | 'E' ) ( '+' | '-' )? Decimal

A floating-point literal has digits on both sides of the point, an exponent, or both. The exponent is a power of ten.

let pi = 3.141592653589793;
let light = 2.998e8;
let charge = 1.6e-19;
let half = 0.5f32;

1. and .5 are not literals; write 1.0 and 0.5. Underscores may separate digits in each part, as in 1_000.25.

SuffixTypeSuffixType
f8float8f80float80
f16float16f128float128
f32float32f256float256
f64float64f512float512

A suffixed literal is read exactly at its own width, so 0.1f128 is the float128 nearest to one tenth rather than a float64 widened. A value too large for the width is an infinity: 300.0f8 is Inf, since a float8 holds at most 240.

An unsuffixed floating-point literal is always a float64. It does not narrow to fit another width: let single: float32 = 0.75; is error: cannot assign 'float64' to 'float32', single * 2.0 is a float64, and with a float16 operand 2.0 is an error. Write 0.75f32, 2.0f32 and 2.0f16.

Boolean literals

true and false are the two boolean literals. Their type is bool, which converts implicitly to the other boolean widths.

let ready = true;
let wide: bool32 = false;

Character literals

CharLiteral ::= CharPrefix? "'" ( Character | Escape ) "'"
CharPrefix  ::= 'c8' | 'c16' | 'c32' | 'c64' | 'c128' | 'c256' | 'c512'

A character literal is exactly one character, or one escape, between single quotes. A prefix, written directly against the quote, chooses the character type:

LiteralTypeHolds
'A'char32Any Unicode scalar value; char8 or char16 by context
c8'A'char8One UTF-8 code unit: U+0000 to U+007F
c16'Ж'char16One UTF-16 code unit that is not a surrogate: up to U+FFFF
c32'😀'char32Any Unicode scalar value
c64'😀'char64Any Unicode scalar value, in eight bytes
c128'😀'char128Any Unicode scalar value, in 16 bytes
c256'😀'char256Any Unicode scalar value, in 32 bytes
c512'😀'char512Any Unicode scalar value, in 64 bytes
let letter = 'A';
let emoji = '😀';
let unit = c8'R';
let word = c16'Ж';
let smile = '\u{263A}';

An unprefixed literal is a char32, except that it becomes a char8 or char16 when it initialises or is assigned to one: let initial: char8 = 'R'; works, and so does return 'Ж'; from a function that returns char16.

A character that does not fit one code unit of its type is refused, never truncated:

let accent = c8'é';
error: character 'é' (U+00E9) does not fit one 'char8' code unit
help: write a string literal such as c8"é", or a byte such as 0xE9u8
MistakeError
''error: character literal is empty
'ab'error: character literal contains more than one character
'a at the end of a lineerror: character literal is not terminated before the end of the line

A prefix is only a prefix when the quote follows at once: c16 'x', with a space, is the identifier c16 followed by a separate literal.

String literals

StringLiteral ::= StringPrefix? '"' ( Character | Escape )* '"'
StringPrefix  ::= 'c8' | 'c16' | 'c32'

A string literal is a read-only slice of character code units — Rux has no separate string type. The prefix chooses the encoding:

LiteralTypeEncoding
"Hello"char8[..]UTF-8
c8"Hello"char8[..]UTF-8
c16"Hello"char16[..]UTF-16
c32"Hello"char32[..]UTF-32

There is no c64, c128, c256 or c512 string: those prefixes make only characters, so c128"x" is the identifier c128 followed by a string literal.

let greeting = "Hello, World!";
let path = "C:\\Rux\\Bin";
let quoted = "say \"hi\"";
let wide = c16"\u{1F680}";   // two UTF-16 code units

.length counts code units of the encoding, not characters: "€".length is 3, c16"€".length is 1. The stored text ends with a NUL code unit that .length does not count, so .data can be passed to C. Text describes string values in full.

A string literal stays on one line. A raw line break or tab inside the quotes is an error — write \n and \t — and so is a missing closing quote:

MistakeError
No closing " before the line endserror: string literal is not terminated before the end of the line
A tab typed inside the quoteserror: string literal contains unescaped control byte 0x09

Escape sequences

Character and string literals share one set of escapes:

EscapeMeaningCode point
\nLine feedU+000A
\rCarriage returnU+000D
\tHorizontal tabU+0009
\vVertical tabU+000B
\fForm feedU+000C
\bBackspaceU+0008
\aAlert (bell)U+0007
\0NULU+0000
\\BackslashU+005C
\'Single quoteU+0027
\"Double quoteU+0022
\u{…}The Unicode scalar value with 1 to 8 hexadecimal digitsas written
let bell = '\a';
let euro = "\u{20AC}";
let rocket = '\u{1F680}';

In a string the \u{…} escape is encoded in the literal's encoding: "\u{20AC}" stores three UTF-8 bytes, c16"\u{20AC}" one UTF-16 unit.

MistakeError
"\x41"error: escape sequence '\x' is not recognized
"C:\Rux"error: escape sequence '\R' is not recognized
"\u41"error: Unicode escape requires '{' after '\u'
"\u{}"error: Unicode escape requires at least one hexadecimal digit
"\u{000000041}"error: Unicode escape contains more than eight hexadecimal digits
'\u{D800}'error: Unicode escape U+D800 is a surrogate, not a scalar value
'\u{110000}'error: Unicode escape U+110000 is above the maximum scalar value U+10FFFF

There is no \x escape. A single byte that is not a character is written as an integer, such as 0xE9u8.

Other values written as keywords

none is the absent value of an optional and null the null pointer. They are keywords rather than literals, and take their type from the context. Arrays ([1, 2, 3]), tuples ((1, "one")) and struct values (Point { x: 1, y: 2 }) are expressions built from other values; see Arrays, Tuples and Structs.

See also