Intrinsic Expressions
An intrinsic expression is a builtin that appears in expression position and produces a value.
intrinsic = "@" IDENT "(" [ intrinsic_arg { "," intrinsic_arg } ] ")" ;
intrinsic_arg = expression | type ;
Intrinsics MAY accept expressions, types, or a combination of both as arguments, depending on the specific intrinsic.
Each intrinsic has a fixed signature specifying the number and types of arguments it accepts.
It is a compile-time error to call an intrinsic with the wrong number of arguments.
It is a compile-time error to use an unknown intrinsic name.
Quick Reference
The following tables list every intrinsic the compiler recognizes, grouped by whether the intrinsic may appear in any expression position (expression intrinsics) or only inside a checked block (unchecked intrinsics, specified in §9.2). This inventory is kept in sync with the compiler's intrinsic registry: the pre-interned names in crates/rue-air/src/sema/known_symbols.rs and the dispatch on them in crates/rue-air/src/sema/analysis.rs. A name that is absent from that registry is rejected as an unknown intrinsic (rule 4.13:5), so any intrinsic the compiler accepts MUST appear here.
Expression intrinsics (usable in any expression position):
| Intrinsic | Purpose | Arguments | Return Type |
|---|---|---|---|
@dbg | Print debug output | 1 expression (int, bool, or string) | () |
@size_of | Get type size in bytes | 1 type | i32 |
@align_of | Get type alignment in bytes | 1 type | i32 |
@int_max | Largest value of an integer type (§4.13:126) | 1 type (integer) | that integer type |
@int_min | Smallest value of an integer type (§4.13:126) | 1 type (integer) | that integer type |
@offset_of | Get a struct field's byte offset | 1 type, 1 field name | u64 |
@intCast | Convert between integer types | 1 expression (integer) | inferred integer type |
@bitCast | Reinterpret an integer's bits at the same width (§4.13:118) | 1 expression (integer) | inferred integer type of the same width |
@wrapping_add | Wrapping (modular) addition (§4.13:97) | 2 expressions (same integer type) | that integer type |
@wrapping_sub | Wrapping (modular) subtraction (§4.13:97) | 2 expressions (same integer type) | that integer type |
@wrapping_mul | Wrapping (modular) multiplication (§4.13:97) | 2 expressions (same integer type) | that integer type |
@to_string | Format an integer as its decimal StrBuf (requires a lexical @import("std") in the file; §3.7:22) | 1 expression (any integer) | StrBuf |
@drop | Run a value's drop glue and consume it (RUE-187) | 1 expression (any type) | () |
@read_line | Read line from stdin | none | Option(StrBuf) |
@parse_i32 | Parse text to i32 | 1 expression (any text rung) | Option(i32) |
@parse_i64 | Parse text to i64 | 1 expression (any text rung) | Option(i64) |
@parse_u32 | Parse text to u32 | 1 expression (any text rung) | Option(u32) |
@parse_u64 | Parse text to u64 | 1 expression (any text rung) | Option(u64) |
@random_u32 | Generate random u32 | none | u32 |
@random_u64 | Generate random u64 | none | u64 |
@arg_count | Number of command-line arguments (incl. argv[0]) | none | u64 |
@arg_len | Byte length of argument i (0 out of range) | 1 expression (u64 index) | u64 |
@env_count | Number of environment entries | none | u64 |
@env_len | Byte length of environment entry i (0 out of range) | 1 expression (u64 index) | u64 |
@target_arch | Get target architecture | none | Arch |
@target_os | Get target OS | none | Os |
@target_data_model | Get target C data model | none | DataModel |
@import | Import module | 1 expression (string literal) | module type |
Unchecked intrinsics (only valid inside a checked block; see §9.2 for their full semantics):
| Intrinsic | Purpose | Arguments | Return Type |
|---|---|---|---|
@syscall | Direct system call | 1–7 expressions (u64) | i64 |
@raw | const pointer to a place | 1 place expression | ptr const T |
@raw_mut | mut pointer to a place | 1 place expression | ptr mut T |
@field_ptr | mut pointer to a struct field place | 1 field-access expression | ptr mut F |
@ptr_read | Read through a pointer | 1 expression (ptr const T/ptr mut T) | T |
@ptr_write | Write through a pointer | 2 expressions (ptr mut T, T) | () |
@ptr_read_unaligned | Read through a possibly unaligned pointer (§9.2) | 1 expression (ptr const T/ptr mut T) | T |
@ptr_write_unaligned | Write through a possibly unaligned pointer (§9.2) | 2 expressions (ptr mut T, T) | () |
@ptr_offset | Pointer arithmetic | 2 expressions (ptr T, integer) | ptr T |
@ptr_to_int | Pointer to integer | 1 expression (pointer) | u64 |
@int_to_ptr | Integer to pointer | 1 expression (u64) | inferred ptr mut T |
@alloc | Allocate physical bytes with alignment (§9.2) | 2 expressions (u64 size, u64 align) | ptr mut u8 |
@alloc_zeroed | Allocate zero-filled physical bytes (§9.2) | 2 expressions (u64 size, u64 align) | ptr mut u8 |
@free | Free an allocated block (§9.2) | 3 expressions (ptr mut u8, u64 size, u64 align) | () |
@realloc | Resize an allocated block, possibly moving it (§9.2) | 4 expressions (ptr mut u8, u64 old size, u64 align, u64 new size) | ptr mut u8 |
@resize | Resize an allocated block in place only (§9.2) | 4 expressions (ptr mut u8, u64 old size, u64 align, u64 new size) | bool |
@byte_copy | Copy size non-overlapping bytes | 3 expressions (ptr mut u8, ptr const u8/ptr mut u8, u64) | () |
@byte_move | Copy size possibly overlapping bytes (§9.2) | 3 expressions (ptr mut u8, ptr const u8/ptr mut u8, u64) | () |
@byte_set | Fill size bytes with a byte | 3 expressions (ptr mut u8, u8, u64) | () |
@arg_ptr | Pointer to argument i's bytes (null out of range) | 1 expression (u64 index) | ptr mut u8 |
@env_ptr | Pointer to environment entry i's bytes (null out of range) | 1 expression (u64 index) | ptr mut u8 |
The compiler frontend additionally reserves the names @cast, @panic, @assert, and @test_preview_gate. Their surface syntax is not yet fully stabilized, so they are omitted from the normative inventory above, but they are no longer no-ops (RUE-319):
@panic(msg?: text)has type!(never): it aborts the process and never returns. The optional message may use any canonical text rung; unrelated aggregates are not accepted. It writespanic: <msg>(or justpanicwhen called with no argument) to standard error and exits with status 101 — the same abort discipline as the@intCastoverflow, division-by-zero, and bounds-check traps. As a diverging expression it participates in never coercion (3.4:2), so it may appear wherever a value of any type is expected — a typedletinitializer, anif/elseormatcharm whose other arms produce a value, or a bare function tail.@assert(cond: bool, msg?: text)requires an exact boolean condition and the same optional text contract as@panic. Whencondisfalseit aborts exactly like@panic: with a message it writespanic: <msg>, otherwise it writesassertion failed, and in both cases exits with status 101. Whencondistrueit has no effect. The expression has type()on both paths.@castis rejected at compile time with a diagnostic directing the programmer to@intCast; it never had a working inference rule and is fully redundant with@intCast. Use@intCastfor integer conversions.@test_preview_gate()is a zero-argument no-op that exists only to test the preview-feature gating machinery itself (--preview test_infra); it is test infrastructure, not a language feature.
@dbg
The @dbg intrinsic prints a value to standard output for debugging purposes. It borrows its argument: the value is read but not consumed, so a non-Copy argument (such as a StrBuf) remains valid after the @dbg call and is dropped by its owner at the end of the enclosing scope, exactly as if the @dbg call had not occurred.
@dbg accepts exactly one argument of integer, boolean, or string type.
@dbg prints the value followed by a newline character.
The textual form @dbg prints for its argument is determined by the argument's type: an integer is printed in base 10, with a leading - when a signed integer is negative and no sign otherwise; a boolean is printed as true or false; a StrBuf is printed as its exact bytes, byte-for-byte, with no quoting or escaping (mirroring print, 3.7). The newline of 4.13:8 follows this text.
The return type of @dbg is ().
fn main() -> i32 {
@dbg(42); // prints: 42
@dbg(-17); // prints: -17
@dbg(true); // prints: true
@dbg(false); // prints: false
@dbg(10 + 5); // prints: 15
@dbg("hello"); // prints: hello
0
}
@dbg is useful for inspecting values during development:
fn factorial(n: i32) -> i32 {
@dbg(n); // trace each call
if n <= 1 {
1
} else {
n * factorial(n - 1)
}
}
fn main() -> i32 {
factorial(5)
}
@size_of
The @size_of intrinsic returns the size of a type in bytes.
@size_of accepts exactly one argument, which MUST be a type.
The return type of @size_of is i32.
The value returned by @size_of is determined at compile time.
fn main() -> i32 {
@size_of(i32) // 4 (i32's natural byte width, observed under the compact layout)
}
struct Point { x: i32, y: i32 }
fn main() -> i32 {
// 8 under the compact layout (ADR-0052): two four-byte i32 fields, no padding.
@size_of(Point) // 8
}
@align_of
The @align_of intrinsic returns the alignment of a type in bytes.
@align_of accepts exactly one argument, which MUST be a type.
The return type of @align_of is i32.
The value returned by @align_of is determined at compile time.
@align_of(T) reports the alignment the implementation has chosen for T under the layout in effect for the compilation (1.3:6, 3.6:12); it observes that choice and does not guarantee a particular value. Under the compact layout (ADR-0052) each scalar has its natural alignment — @align_of(i32) observes 4, @align_of(bool) observes 1, @align_of(i64) observes 8 — and a struct's alignment is that of its most-aligned field, so a portable program must not assume a particular value such as 8. Whichever layout is in effect, the size of a type is always a multiple of its alignment (3.6:8).
fn main() -> i32 {
@align_of(i32) // 4 (i32's natural alignment, observed under the compact layout)
}
@int_max and @int_min
The @int_max intrinsic returns the largest value representable in an integer type, and the @int_min intrinsic returns the smallest. The bounds follow the two's-complement ranges of §3.1: for an unsigned type of width w they are 2^w - 1 and 0; for a signed type they are 2^(w-1) - 1 and -2^(w-1).
@int_max and @int_min each accept exactly one argument, which MUST be an integer type (§3.1).
The result type of @int_max(T) and @int_min(T) is T itself. This is the only result type that can represent every bound exactly: @int_max(u64) exceeds every signed type, and @int_min(i64) is below every unsigned type.
It is a compile-time error (E0702) to apply @int_max or @int_min to a type argument that is not an integer type.
The value of @int_max(T) and @int_min(T) is determined at compile time, and — unlike @size_of and @align_of — the integer-bounds intrinsics are comptime-evaluable (4.14:29): the bounds depend only on the identity of T, never on layout, so they may appear in const initializers and comptime argument positions.
fn main() -> i32 {
if @int_max(u8) == 255 && @int_min(i8) == -128 {
0
} else {
1
}
}
Because the result is typed at the queried type, the intrinsics compose with generic comptime T code: overflow predicates such as a > @int_max(T) - b need no per-type constants, which is the motivating use (generic checked/saturating arithmetic, RUE-694).
@offset_of
The @offset_of intrinsic returns the byte offset of a field within a struct type, mirroring Rust's core::mem.offset_of!.
@offset_of accepts exactly two arguments: the first MUST be a struct type, and the second MUST be the name of one of that struct's fields.
The return type of @offset_of is u64.
The value returned by @offset_of is the offset the compiler assigns to the field under the layout it chooses for the struct, determined at compile time. Because the value comes from the compiler's own layout rather than a hand-computed constant, @offset_of remains correct even if the struct layout is implementation-defined. Under the compact layout each field is placed at the lowest offset satisfying its alignment after the preceding fields, so an offset accounts for both the preceding fields' sizes and any alignment padding (§3.6).
It is a compile-time error to apply @offset_of to a non-struct type, or to name a field that the struct does not declare.
struct Mixed { a: i32, b: i64, c: bool }
fn main() -> i32 {
let off_a: u64 = @offset_of(Mixed, a); // 0
let off_b: u64 = @offset_of(Mixed, b); // 8 (i64 field at its eight-byte alignment)
let off_c: u64 = @offset_of(Mixed, c); // 16 (bool after the i64)
let sum: u64 = off_a + off_b + off_c; // 24
@intCast(sum)
}
@intCast
The @intCast intrinsic converts an integer value from one integer type to another.
@intCast accepts exactly one argument, which MUST be an integer type (any of i8, i16, i32, i64, u8, u16, u32, u64).
The target type of the conversion is inferred from the context where @intCast is used.
It is a compile-time error if the target type cannot be inferred or is not an integer type.
If the source value cannot be exactly represented in the target type, a runtime panic occurs.
fn main() -> i32 {
let x: i32 = 100;
let y: u8 = @intCast(x); // OK: 100 fits in u8
@intCast(y) // Convert back to i32
}
fn takes_u8(x: u8) -> u8 { x }
fn main() -> i32 {
let x: i32 = 50;
takes_u8(@intCast(x)); // Target type inferred from parameter
0
}
// This panics at runtime: 256 doesn't fit in u8
fn main() -> i32 {
let x: i32 = 256;
let y: u8 = @intCast(x); // panic: integer cast overflow
0
}
// This panics at runtime: negative values don't fit in unsigned types
fn main() -> i32 {
let x: i32 = -1;
let y: u32 = @intCast(x); // panic: integer cast overflow
0
}
@bitCast
The @bitCast intrinsic reinterprets an integer value's bits at another integer type of the same width. It is the bit-preserving counterpart to @intCast, which is value-preserving: @intCast keeps the number and rejects the representations that do not fit, while @bitCast keeps the representation and lets the number change.
@bitCast accepts exactly one argument, which MUST be of an integer type (any of i8, i16, i32, i64, u8, u16, u32, u64). The target type is inferred from the context where @bitCast is used, exactly as @intCast's target type is (4.13:26).
It is a compile-time error if the target type cannot be inferred, is not an integer type, or is an integer type whose width differs from the argument's width (E0950). A reinterpretation neither invents nor discards bits, so it is defined only between the same-width pairs — i8/u8, i16/u16, i32/u32, and i64/u64 — in either direction, and between an integer type and itself. Converting between widths is @intCast's job.
For a source type of width N bits, the value of @bitCast(x) is the value whose N-bit two's-complement representation is identical to that of x. Equivalently, for a target type T: when the shared width is N and the argument's value is x, the result is x if x is in range for T, and otherwise x - 2^N when T is signed and x + 2^N when T is unsigned.
@bitCast never traps. Every value of the source type has a representation at the target type — the same representation — so no operand is rejected, in contrast to the overflow trap of 4.13:28. In particular @bitCast is the operation that moves a u64 whose top bit is set into an i64, which @intCast traps on.
@bitCast is an involution on each same-width pair: for every value x of an integer type S and same-width type T, reinterpreting x at T and then reinterpreting that result back at S yields x.
In a compile-time context, @bitCast is evaluated exactly as @intCast is: it introduces no new constant-evaluation rule, so an invocation that @intCast would not fold into a constant is likewise not a constant expression.
fn main() -> i32 {
// The u64 values above i64::MAX have no @intCast into i64 at all;
// @bitCast moves them across, and back, unchanged.
let big: u64 = 9223372036854775808; // 1 << 63
let signed: i64 = @bitCast(big);
@dbg(signed); // -9223372036854775808
let back: u64 = @bitCast(signed);
@dbg(back); // 9223372036854775808
// Narrower pairs reinterpret the same way.
let byte: u8 = 255;
let sbyte: i8 = @bitCast(byte);
@dbg(sbyte); // -1
0
}
@read_line
The @read_line intrinsic reads a line of text from standard input.
@read_line accepts no arguments.
The return type of @read_line is the trusted standard Option specialized at StrBuf — the producer-nominal enum declared by std.option.Option (std/option.rue, ADR-0038), instantiated as Option(StrBuf). The intrinsic yields this exact standard specialization in every context: bare as the operand of the ? operator (§4.15), as the initializer of an annotated let, as the scrutinee of a match, or as a freestanding expression. Surrounding context never selects which nominal the intrinsic produces; when an annotation or match is present it only checks that the intrinsic's standard Option(StrBuf) matches, and any other type — including a user-defined enum that repeats the Some/None shape under a different producer — is an ordinary type error (E0702). Because the standard Option is a toolchain guarantee, @read_line has this type even in a program that does not import std lexically; the compiler roots the trusted-module demand itself, and an absent standard library is a toolchain-integrity error rather than a language state.
@read_line reads bytes from standard input until a newline character (\n) is encountered or end-of-file is reached.
On a successful read the result is Some(line), where the line StrBuf does not include the trailing newline character.
If end-of-file is reached with some data read, the partial line is returned as Some(line).
If end-of-file is reached with no data read, the result is None (this is not an error; a read-until-end-of-input loop terminates by observing None).
If a read error occurs, a runtime panic occurs with the message "input error". If allocation or capacity growth fails while constructing the returned StrBuf, the allocation-failure rules of §8.6 apply. (These behaviors are documented but not tested here, as the failures cannot be reliably simulated through portable source-level input.)
const std = @import("std");
const Opt = std.option.Option(std.strbuf.StrBuf);
fn main() -> i32 {
@dbg("What is your name?");
match @read_line() {
Opt.Some(name) => @dbg(name),
Opt.None => @dbg("(no input)"),
}
0
}
Reading every line until end-of-input:
const std = @import("std");
const Opt = std.option.Option(std.strbuf.StrBuf);
fn main() -> i32 {
loop {
let line: Opt = @read_line();
match line {
Opt.None => break,
Opt.Some(text) => @dbg(text),
}
}
0
}
Integer Parsing Intrinsics
The integer parsing intrinsics convert a string to an integer value.
Each parsing intrinsic returns the trusted standard Option specialized at its target integer type T — the producer-nominal enum declared by std.option.Option (std/option.rue, ADR-0038):
@parse_i32returnsOption(i32)@parse_i64returnsOption(i64)@parse_u32returnsOption(u32)@parse_u64returnsOption(u64)
The intrinsic yields this exact standard specialization in every context — bare as the operand of the ? operator (§4.15), as the initializer of an annotated let, as the scrutinee of a match, or as a freestanding expression. Surrounding context never selects the nominal; when present it only checks that the intrinsic's standard Option(T) matches, and any other type — including a user-defined Some/None lookalike under a different producer — is an ordinary type error (E0702). As with @read_line (rule 4.13:35), the compiler roots the trusted standard Option itself, so a parsing intrinsic has this type even without a lexical std import.
Each parsing intrinsic accepts exactly one argument, which MUST be one of the text types str, Str(N), or StrBuf.
The string argument is borrowed, not consumed. The original string remains valid after parsing.
A successful parse yields Some(n). The parsed string is parsed successfully when it matches the following grammar:
integer_string = [ "-" ] digit { digit } ;
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
Leading minus signs are only allowed for signed types (@parse_i32, @parse_i64); a negative value for an unsigned type is a parse failure (yields None).
The result is None (a recoverable parse failure, not a panic) if:
- The string is empty
- The string contains non-digit characters (other than an optional leading minus)
- The value overflows the target type
- A negative value is parsed for an unsigned type
const std = @import("std");
fn main() -> i32 {
let Opt = std.option.Option(i32);
match @parse_i32("42") {
Opt.Some(n) => n, // returns 42
Opt.None => 0,
}
}
const std = @import("std");
fn main() -> i32 {
let Opt = std.option.Option(i32);
match @parse_i32("-17") {
Opt.Some(n) => n, // returns -17
Opt.None => 0,
}
}
const std = @import("std");
fn main() -> i32 {
let Opt = std.option.Option(i32);
let s = "42";
// StrBuf is borrowed, not consumed
let parsed: Opt = @parse_i32(s);
@dbg(s); // s is still valid
match parsed {
Opt.Some(n) => n,
Opt.None => 0,
}
}
// An invalid character is a recoverable failure: `None`, not a panic.
const std = @import("std");
fn main() -> i32 {
let Opt = std.option.Option(i32);
match @parse_i32("12abc") {
Opt.Some(n) => n,
Opt.None => -1, // taken: "12abc" is not an integer
}
}
// A negative value for an unsigned type is a recoverable failure: `None`.
const std = @import("std");
fn main() -> i32 {
let Opt = std.option.Option(u32);
match @parse_u32("-17") {
Opt.Some(n) => @intCast(n),
Opt.None => 0, // taken: "-17" is negative
}
}
@random_u32
The @random_u32 intrinsic generates a random unsigned 32-bit integer.
@random_u32 accepts no arguments.
The return type of @random_u32 is u32.
Each call to @random_u32 returns a non-deterministic value using a platform-provided cryptographically-secure entropy source.
If the platform entropy source is unavailable or fails, a runtime panic occurs.
fn main() -> i32 {
let secret: u32 = (@random_u32() % 100) + 1; // Random number 1-100
@dbg(secret);
0
}
Using @random_u32 in a guessing game:
const std = @import("std");
const StrBuf = std.strbuf.StrBuf;
fn main() -> i32 {
let OptStr = std.option.Option(StrBuf);
let OptU32 = std.option.Option(u32);
let secret: u32 = (@random_u32() % 100) + 1; // 1-100
@dbg("Guess the number between 1 and 100!");
let mut guesses = 0;
loop {
let input: OptStr = @read_line();
match input {
OptStr.None => break, // end of input
OptStr.Some(text) => {
match @parse_u32(text) {
OptU32.Some(guess) => {
guesses = guesses + 1;
if guess < secret {
@dbg("Too low!");
} else if guess > secret {
@dbg("Too high!");
} else {
@dbg("You got it!");
break;
}
},
OptU32.None => @dbg("not a number, try again"),
}
},
}
}
@intCast(guesses)
}
@random_u64
The @random_u64 intrinsic behaves identically to @random_u32 but returns a random unsigned 64-bit integer.
@random_u64 accepts no arguments.
The return type of @random_u64 is u64.
fn main() -> i32 {
let large_random = @random_u64();
@dbg(large_random);
0
}
@arg_count, @arg_len, @arg_ptr
The @arg_count, @arg_len, and @arg_ptr intrinsics expose the command-line arguments the platform loader supplied to the process at entry. They are the low-level surface on which std.env builds its owned-StrBuf accessors; the argument vector is a fixed process input, so a program observes the same arguments for the whole of its execution.
@arg_count accepts no arguments and returns a u64: the number of command-line arguments, including argv[0] (the program invocation path). A process is always launched with at least one argument, so the result is at least 1.
@arg_len accepts one u64 index and returns a u64: the length in bytes of argument i, not counting any terminator. @arg_ptr accepts one u64 index and returns ptr mut u8, a pointer to the first of that argument's bytes. Because @arg_ptr yields a raw pointer, it may appear only inside a checked block (§9.2), exactly like @alloc; @arg_count and @arg_len impose no such requirement.
When the index passed to @arg_len or @arg_ptr is greater than or equal to @arg_count(), @arg_len returns 0 and @arg_ptr returns a null pointer. For an in-range index, the @arg_ptr pointer addresses exactly @arg_len(i) readable bytes. Argument bytes are not interpreted as UTF-8 (ADR-0035).
fn main() -> i32 {
// A program invoked with no extra arguments still sees argv[0].
@dbg(@arg_count() >= 1); // true
0
}
@env_count, @env_len, @env_ptr
The @env_count, @env_len, and @env_ptr intrinsics expose the process environment the platform loader supplied at entry, as a sequence of KEY=VALUE byte strings. They mirror the @arg_* intrinsics and back std.env's environment lookups.
@env_count accepts no arguments and returns a u64: the number of environment entries. @env_len accepts one u64 index and returns a u64: the length in bytes of environment entry i. @env_ptr accepts one u64 index and returns ptr mut u8, a pointer to the first of that entry's bytes; like @arg_ptr, it may appear only inside a checked block (§9.2), while @env_count and @env_len may appear in any expression position.
When the index passed to @env_len or @env_ptr is greater than or equal to @env_count(), @env_len returns 0 and @env_ptr returns a null pointer. For an in-range index, the @env_ptr pointer addresses exactly @env_len(i) readable bytes, which encode one KEY=VALUE pair. Environment bytes are not interpreted as UTF-8 (ADR-0035).
fn main() -> i32 {
// The environment is captured once; entries can be scanned by index.
let mut i: u64 = 0;
while i < @env_count() {
i = i + 1;
}
0
}
@target_arch
The @target_arch intrinsic returns the target architecture as an Arch enum value.
@target_arch accepts no arguments.
The return type of @target_arch is Arch.
The Arch enum is a built-in enum with the following variants:
Arch.X86_64- x86-64 architectureArch.Aarch64- ARM64/AArch64 architecture
The value returned by @target_arch is determined at compile time based on the compilation target.
fn main() -> i32 {
match @target_arch() {
Arch.X86_64 => 1,
Arch.Aarch64 => 2,
}
}
@target_os
The @target_os intrinsic returns the target operating system as an Os enum value.
@target_os accepts no arguments.
The return type of @target_os is Os.
The Os enum is a built-in enum with the following variants:
Os.Linux- Linux operating systemOs.Macos- macOS operating system
The value returned by @target_os is determined at compile time based on the compilation target.
fn main() -> i32 {
match @target_os() {
Os.Linux => 1,
Os.Macos => 2,
}
}
Combining @target_arch and @target_os for platform-specific code:
fn main() -> i32 {
match @target_arch() {
Arch.X86_64 => {
match @target_os() {
Os.Linux => 99,
Os.Macos => 88,
}
},
Arch.Aarch64 => {
match @target_os() {
Os.Linux => 77,
Os.Macos => 66,
}
},
}
}
@target_data_model
The @target_data_model intrinsic returns the compilation target's C data model — the width convention the target's C ABI assigns to int, long, and pointers, which selects the widths of the std.c transparent scalar aliases (ADR-0064 Amendment 1) — as a DataModel enum value.
@target_data_model accepts no arguments.
The return type of @target_data_model is DataModel.
The DataModel enum is a built-in enum with the following variants:
DataModel.Ilp32-int,long, and pointers are 32-bitDataModel.Llp64-long longand pointers are 64-bit;longremains 32-bitDataModel.Lp64-longand pointers are 64-bit
The value returned by @target_data_model is determined at compile time based on the compilation target. Every target the reference compiler currently supports (the x86-64 and AArch64 Linux/macOS targets reachable through @target_arch/@target_os) is Lp64.
fn main() -> i32 {
match @target_data_model() {
DataModel.Lp64 => 1,
DataModel.Llp64 => 2,
DataModel.Ilp32 => 3,
}
}
@import
The @import intrinsic imports a module from another source file.
@import accepts exactly one argument, which MUST be a string literal specifying the module path.
The return type of @import is a module struct type containing all pub declarations from the imported file.
Module path resolution follows this order:
- Standard library:
@import("std")resolves to the bundled standard library - A file
{path}.ruerelative to the importing file's directory - A directory module: a directory
{path}/containing the facade file_{basename}.rue, which is the module's root
It is a compile-time error if the module path does not resolve to an existing file.
It is a compile-time error to pass a non-string-literal argument to @import.
// math.rue
pub fn add(a: i32, b: i32) -> i32 { a + b }
pub fn sub(a: i32, b: i32) -> i32 { a - b }
fn helper() -> i32 { 42 } // private, not exported
// main.rue
fn main() -> i32 {
let math = @import("math.rue");
math.add(1, 2) // returns 3
}
Private declarations (those without pub) are not visible to importers:
// main.rue
fn main() -> i32 {
let math = @import("math.rue");
// math.helper() // Error: `helper` is not visible
0
}
The imported module can be bound to any name:
fn main() -> i32 {
let m = @import("math.rue");
m.add(1, 2)
}
Nested paths are supported for importing from subdirectories:
fn main() -> i32 {
let strings = @import("utils/strings.rue");
0
}
It is a compile-time error (E0713) if a relative import path's normalized candidate falls outside the project root (the root file's directory); see rule 10.2:7. Module identity is project-root-relative, so a file outside the root can receive no identity.
It is a compile-time error to access a member of a module that is not declared in the imported file. Module membership is per-file: even though all compiled files share one global function namespace, a declaration from some other file is not a member of the module and MUST NOT be reachable through it.
@wrapping_add, @wrapping_sub, @wrapping_mul
The @wrapping_add, @wrapping_sub, and @wrapping_mul intrinsics each take exactly two operands and compute, respectively, the sum, difference, and product of those operands with two's-complement wraparound instead of the overflow trap of the +, -, and * operators. Each is a non-trapping counterpart to the corresponding checked operator.
Both operands and the result of a wrapping-arithmetic intrinsic share a single integer type, established by the same equality-and-integer constraints as the checked +, -, and * operators (§4.2). It is a compile-time error to apply one to a non-integer operand, and it is a compile-time error for the two operands to have differing integer types.
The intrinsics are defined for every integer type — the signed widths i8, i16, i32, i64 and the unsigned widths u8, u16, u32, u64. For a result type of width N bits, the value of the intrinsic is the true mathematical result reduced modulo 2^N and interpreted as a two's-complement value of the result type. Equivalently, the result is congruent to the exact mathematical result modulo 2^N and lies within the range of the result type.
A wrapping-arithmetic intrinsic never traps: for every combination of operand values, including those for which the checked operator would trap on overflow, it produces the reduced value defined by 4.13:93. Because the low N bits of a two's-complement product do not depend on the signedness of the operands, @wrapping_mul yields the same bit pattern for a signed and an unsigned type of the same width.
In a compile-time context, a wrapping-arithmetic intrinsic is evaluated exactly as @intCast is: the intrinsics introduce no new constant-evaluation rule, so an invocation that @intCast would not fold into a constant is likewise not a constant expression.
fn main() -> i32 {
// 127 + 1 wraps to the minimum i8 value.
let hi: i8 = 127;
let one: i8 = 1;
@dbg(@wrapping_add(hi, one)); // -128
// 0 - 1 wraps to the maximum u8 value.
let zero: u8 = 0;
let uone: u8 = 1;
@dbg(@wrapping_sub(zero, uone)); // 255
// The FNV-1a hashing step that overflows u64.
let hash: u64 = 14695981039346656037;
let prime: u64 = 1099511628211;
@dbg(@wrapping_mul(hash, prime)); // 12638153115695167455
0
}