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 table provides a quick reference to all available intrinsics:
| 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 |
@intCast | Convert between integer types | 1 expression (integer) | inferred integer type |
@read_line | Read line from stdin | none | String |
@parse_i32 | Parse string to i32 | 1 expression (String) | i32 |
@parse_i64 | Parse string to i64 | 1 expression (String) | i64 |
@parse_u32 | Parse string to u32 | 1 expression (String) | u32 |
@parse_u64 | Parse string to u64 | 1 expression (String) | u64 |
@random_u32 | Generate random u32 | none | u32 |
@random_u64 | Generate random u64 | none | u64 |
@target_arch | Get target architecture | none | Arch |
@target_os | Get target OS | none | Os |
@import | Import module | 1 expression (string literal) | module type |
@dbg
The @dbg intrinsic prints a value to standard output for debugging purposes.
@dbg accepts exactly one argument of integer, boolean, or string type.
@dbg prints the value followed by a newline character.
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) // 8 (one 8-byte slot)
}
struct Point { x: i32, y: i32 }
fn main() -> i32 {
@size_of(Point) // 16 (two 8-byte slots)
}
@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.
All types in Rue currently have 8-byte alignment.
fn main() -> i32 {
@align_of(i32) // 8
}
@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
}
@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 String.
@read_line reads bytes from standard input until a newline character (\n) is encountered or end-of-file is reached.
The returned String does not include the trailing newline character.
If end-of-file is reached with some data read, the partial line is returned.
If end-of-file is reached with no data read, a runtime panic occurs with the message "unexpected end of input".
If a read error occurs, a runtime panic occurs with the message "input error". (This behavior is documented but not tested, as I/O errors cannot be reliably simulated in portable test environments.)
fn main() -> i32 {
@dbg("What is your name?");
let name = @read_line();
@dbg("Hello, ");
@dbg(name);
0
}
Reading multiple lines:
fn main() -> i32 {
let line1 = @read_line(); // First line
let line2 = @read_line(); // Second line
@dbg(line1);
@dbg(line2);
0
}
Integer Parsing Intrinsics
The integer parsing intrinsics convert a string to an integer value.
The following parsing intrinsics are available:
@parse_i32returnsi32@parse_i64returnsi64@parse_u32returnsu32@parse_u64returnsu64
Each parsing intrinsic accepts exactly one argument, which MUST be of type String.
The string argument is borrowed, not consumed. The original string remains valid after parsing.
The parsed string must match 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 runtime panic occurs 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
fn main() -> i32 {
let s = "42";
let n = @parse_i32(s);
n // returns 42
}
fn main() -> i32 {
let s = "-17";
let n = @parse_i32(s);
n // returns -17
}
fn main() -> i32 {
let s = "42";
// String is borrowed, not consumed
let n = @parse_i32(s);
@dbg(s); // s is still valid
n
}
// This panics at runtime: invalid character
fn main() -> i32 {
let s = "12abc";
let n = @parse_i32(s); // panic: invalid character
n
}
// This panics at runtime: negative for unsigned
fn main() -> i32 {
let s = "-17";
let n: u32 = @parse_u32(s); // panic: negative value for unsigned type
@intCast(n)
}
@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:
fn main() -> i32 {
let secret: u32 = (@random_u32() % 100) + 1; // 1-100
@dbg("Guess the number between 1 and 100!");
let mut guesses = 0;
loop {
let input = @read_line();
let guess = @parse_u32(input);
guesses = guesses + 1;
if guess < secret {
@dbg("Too low!");
} else if guess > secret {
@dbg("Too high!");
} else {
@dbg("You got it!");
break;
}
}
@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
}
@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,
}
},
}
}
@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
_{path}.ruewith subdirectory{path}/
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");
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");
// math.helper() // Error: `helper` is not visible
0
}
The imported module can be bound to any name:
fn main() -> i32 {
let m = @import("math");
m.add(1, 2)
}
Nested paths are supported for importing from subdirectories:
fn main() -> i32 {
let strings = @import("utils/strings");
0
}