Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

数据类型

Riddle 是静态类型语言。每个绑定、参数、返回值和表达式都会在编译期得到一个具体类型;多数局部绑定可以从初始化表达式推断,函数边界通常显式写出类型。

本章只介绍日常代码首先需要的类型。结构体、枚举、trait、callable、底层 ABI 和属性分别放在后续专题中。

标量类型

类型含义
i8i16i32i64isize有符号整数
u8u16u32u64usize无符号整数
f32f64浮点数
booltruefalse
charUnicode 标量值
()unit 类型及其唯一值
!永不产生值的 never 类型

isizeusize 跟随目标指针宽度。unit 不是 Riddle 类型名,空结果写作 ()

字面量与后缀

数值字面量可以显式指定类型:

let integer = 42i32;
let byte = 255u8;
let mask = 0xff_00u16;
let permissions = 0o755;
let flags = 0b1010_0101u8;
let single = 3.14f32;
let double = 1.0f64;

整数字面量支持十进制、0x 十六进制、0o 八进制和 0b 二进制;各形式都可以使用 _ 分隔符和整数类型后缀。

词法器会额外接受 i128u128f16f128 后缀,但类型系统只支持 8 到 64 位的整数和 f32 / f64,这些后缀会在类型检查时报 E0011

整数算术按目标位宽回绕。除零以及有符号最小值除以 -1 会终止进程;移位计数按位宽取模。有符号右移使用算术右移。

char 与数值转换

char as Integer 得到 Unicode 码点。u8 as char 也受支持,因为任意 u8 都是合法 Unicode 标量值;其他整数不能直接转换成 char

let code_point = '中' as u32; // 20013
let letter = 65u8 as char;    // 'A'

更完整的 as 转换范围见表达式与块

Never 类型

! 表示表达式永远不会正常产生值。标准宏 panic!(...)todo!(...)unimplemented!(...)unreachable!(...) 返回 !,所以可以出现在需要其他结果类型的分支:

fun require(valid: bool) -> i32 {
    if valid { 42 } else { panic!("invalid state: {}", valid) }
}

当前 panic 运行时会输出源位置和消息,然后调用 C abort();它不会进行栈展开或恢复。可恢复失败应使用 OptionResult,详见错误处理

元组

元组是定长异构值。逗号用于区分元组与普通分组括号:

let pair: (i32, bool) = (42, true);
let single: (i32,) = (42,);
let unit: () = ();

元组元素也可以用数字字段访问,索引从 0 开始:

let number = pair.0;
let flag = pair.1;

元组可以通过模式拆开:

let (number, flag) = pair;

固定长度数组

数组类型写作 [T; N],元素类型与长度都在编译期确定:

let values: [i32; 3] = [1, 2, 3];
let zeros: [i32; 3] = [0; 3];

let mut pair = [10, 20];
pair[0] = 99;

数组和切片的普通索引会检查边界,越界时向 stderr 输出 riddle: index out of bounds 并终止进程。需要可恢复的访问时,先借用为切片,再使用 getget_mut

数组长度可以来自 const 泛型参数:

struct Buffer<T, const N: usize> {
    data: [T; N],
}

let buffer: Buffer<i32, 3> = Buffer { data: [1, 2, 3] };

引用

引用暂时访问一个值而不取得所有权:

  • &T 是共享引用,可以同时存在多个;
  • &mut T 是独占可变引用,存活期间不能再创建冲突引用。
let mut value = 42;
let shared: &i32 = &value;
let copied = *shared;

let exclusive: &mut i32 = &mut value;
*exclusive = 43;

借用检查、自动重借用和引用来源将在引用与逃逸详细解释。

切片与不定长类型

[T]str 是不定长类型,不能直接作为局部变量、参数、返回值或普通字段,必须位于引用或原始指针后。对它们的引用同时携带数据地址和长度:

let values = [1, 2, 3];
let slice: &[i32] = &values;
let text: &str = "hello";

切片提供 lenis_emptygetget_mutiteriter_mut;共享和可变切片引用都可用于 for&[T] / &mut [T] 可由对应可变性的数组引用自动转换。

字符串

str 表示不定长的字符串内容,&str 表示可传递的字符串引用值。裸 str 只能作为引用、原始指针或 impl 的目标,不能作为独立值使用。

&str 是对 str 的引用,由两个机器字组成:指向 UTF-8 数据的指针和字节长度(胖指针)。字符串字面量 "..." 的类型就是 &str

let greeting: &str = "hello";

如果字符串内容里包含很多引号或反斜杠,可以使用 raw string。raw string 不解释转义,结束符由 # 的数量决定:

let a: &str = r"hello";
let b: &str = r#"say "hello""#;
let c: &str = r###"content with "# inside"###;

#[lang = r#"copy"#] 这类属性字符串也支持 raw string。

&str 可以自由地在函数之间传递——它只是一个胖指针值:

fun greet(name: &str) {
    // name 是调用方传入的字符串切片的借用
}

fun main() {
    greet("Riddle");
}

&str 通过标准库提供字节长度、空值判断和字节切片视图:

let text: &str = "hello";
let length = text.len();                    // 5usize(UTF-8 字节数,不是字符数)
let empty = text.is_empty();                // false
let bytes = text.as_bytes();                // &[u8]

for ch in "A中🙂" {
    // ch: char,按 UTF-8 解码
}

as_bytes 不复制数据,返回共享字节切片。&str 可直接用于 for,迭代器按 UTF-8 解码并依次产出 Unicode char

可增长的 String 属于集合,见集合&str 在 C 后端的 ABI 表示见FFI 与底层工具链

结构体、枚举与泛型类型

用户可以定义带字段的结构体和带变体的枚举:

struct Pair<A, B> {
    first: A,
    second: B,
}

enum Slot<T> {
    Empty,
    Value(T),
}

嵌套泛型不需要在 > 之间插入空格:

let value: Slot<Pair<i32, bool>> = Slot::Value(Pair {
    first: 1,
    second: true,
});

函数、trait、impl、结构体与枚举的类型参数都可以直接带 trait bound,也可以使用 where 子句。const 参数声明自己的整数类型,当前主要用于数组长度。构造、模式匹配与行为实现分别见结构体枚举、模式与 matchTraitimpl 块

原始指针

*const T*mut T 是供 FFI 与底层实现使用的原始指针,不参与普通引用的借用跟踪。解引用和索引必须位于 unsafe 中:

let pointer = 0usize as *const i32;
// unsafe { *pointer } 会解引用空指针,不要执行

原始指针不是绕过类型、移动或可变性检查的通用工具。完整安全边界和 C 类型映射见FFI 与底层工具链

继续阅读

匿名函数与 Fn / FnMut / FnOnce 的完整规则见闭包与迭代器。可增长的 String 和其他容器见集合。当前完整类型能力与限制见当前工具链状态