Data types and conversions
This topic is not available in the selected language or version. Showing the start of this section.
A type determines which values a variable can hold and which operations apply. This chapter explains numeric widths, explicit conversions, and places where inference needs help. Read Variables and values first.
Choose a built-in type
Section titled “Choose a built-in type”| Type | Use and constraints |
|---|---|
Int (I64) | Default integer type for whole-number calculations. |
I8, I16, I32, I64 | Signed 8-, 16-, 32-, and 64-bit integers. |
U8, U16, U32, U64 | Unsigned integers. For example, U8 ranges from 0 to 255. |
Float (F64), F32 | Calculations with a fractional part. |
Bool | true or false; required for conditions. |
String | An immutable sequence of bytes. |
Unit | No result; its value is (). |
Without a determining context, integer literals default to Int and floating literals to Float. An annotation such as let count: I32 = 42; supplies that context. A variable that already has a numeric type is not implicitly converted to another one.
Convert between numeric types
Section titled “Convert between numeric types”Save this as data_types.xen. i64, u8, and float create values of the requested type; they do not change the type of the original variable.
fn main() { let narrow: I32 = 42; let wide: Int = i64(narrow); let small: U8 = u8(255); let half: Float = float(wide) / 2.0; println(wide); println(small); println(half); println(5 / 2); println(float(5) / 2.0); println("한".len());}xen check data_types.xenxen run data_types.xen4225521.022.535 / 2 produces 2 because both operands are integers. Convert before division when you need a fraction: float(5) / 2.0. Writing float(5 / 2) converts the already computed integer 2; it cannot recover the discarded fraction.
The final 3 is the number of UTF-8 bytes in "한". String length is not a character count, and character indexing is unavailable.
Fix a type mismatch
Section titled “Fix a type mismatch”This complete program deliberately fails. Save it as numeric_mismatch.xen and run xen check numeric_mismatch.xen.
fn main() { let wide: Int = 42; let narrow: I32 = wide; println(narrow);}The diagnostic includes this text; paths and line numbers are omitted:
expected I32, found Int (I64)Change the declaration to let narrow: I32 = i32(wide);. When converting to a smaller type, also consider whether the value fits its range.
Distinguish conversion failure from arithmetic wrapping
Section titled “Distinguish conversion failure from arithmetic wrapping”An out-of-range conversion behaves differently from overflowing integer addition.
u8(255)succeeds, while an out-of-range literal such asu8(256)is rejected during checking.- A conversion of an out-of-range value at runtime terminates with a runtime error.
- Integer
+,-, and*wrap at the type’s limits. Conversion checks do not imply that arithmetic operations have the same checks. - Division by zero, and division of a signed minimum by
-1, are runtime errors.
The following program passes xen check conversion_error.xen but fails under xen run conversion_error.xen:
fn main() { let value = 256; println(u8(value));}numeric conversion to U8 failedSupply missing type information
Section titled “Supply missing type information”An empty collection or a variant with no payload cannot identify its item type:
let values: Vec<Int> = [];let missing: Option<String> = Option.None;Function parameters and return types are explicit too. Generic functions infer type arguments from arguments, rather than just the receiving variable’s type. Generics and type inference explains that distinction.
Choose a composite type
Section titled “Choose a composite type”| Representation needed | Choice |
|---|---|
| Many values of one type | Vec<T> |
| Different values grouped by position | A tuple such as (Int, String) |
| Named fields | struct |
| One of several forms | enum, Option<T>, Result<T, E> |
| A temporary read-only view without copying | Slice<T> |
| An owned recursive connection | Box<T> |
These choices also affect copying and how long values remain usable. Continue with structs and enums, copies, moves, and references, and slices and iterators. Use the type reference for the rules.
Try it
Section titled “Try it”Replace float(5) / 2.0 with float(5 / 2) and explain the changed output. Choose a value for U8, then consider whether its real input could exceed 255.