Chapter 03

Mojo 类型系统

掌握 Mojo 的强类型机制,从基础类型到 struct 结构体,构建高性能数据结构

变量声明:var、let 与 Python def

三种声明方式对比

Mojo 引入了两个新关键字 varlet,与 Python 的隐式赋值形成鲜明对比:

声明方式可变性类型声明编译期保证用途
Python 赋值 x = 1可变可选(类型注释)动态脚本代码
var x = 1可变可选(可推断)类型固定Mojo 普通变量
let x = 1不可变可选(可推断)类型 + 不可变常量值,更多优化
fn variable_demo():
    # var:可变变量,类型可以推断
    var x = 10          # 推断为 Int
    var y: Float64 = 3.14  # 显式类型
    x = 20              # 合法:var 可以重新赋值

    # let:不可变变量(类似 Rust 的 let)
    let pi: Float64 = 3.14159
    # pi = 3.0  # 编译错误:cannot assign to let-declared variable

    # fn 函数:必须声明参数类型
    print(x, y, pi)
为什么 let 能提升性能?

当编译器知道一个变量不会被修改(let),它可以做更激进的优化:将值内联到寄存器、消除不必要的内存访问、更好的别名分析。对于计算密集的循环,使用 let 声明循环不变量(loop-invariant)可以有效提升性能。

基础类型详解

数值类型

Int
平台原生有符号整数(64-bit 平台上为 64-bit,32-bit 平台为 32-bit)。与 Python int 的区别:Mojo Int 有固定位宽,不是任意精度大整数。
Int8/16/32/64
固定位宽有符号整数。Int8 范围 -128~127,Int64 范围约 ±9.2×10¹⁸。在 SIMD 操作和底层内存操作中常用。
UInt8/16/32/64
固定位宽无符号整数。UInt8 范围 0~255,常用于图像像素处理(RGB 值)。
Float16/32/64
IEEE 754 浮点数。Float32 是深度学习最常用的训练精度,Float16 用于推理加速(半精度),Float64 用于科学计算(双精度)。
Bool
布尔类型,值为 TrueFalse。与 Python bool 完全兼容。
fn numeric_types():
    # 整数类型
    var a: Int = 42
    var b: Int32 = -100
    var pixel: UInt8 = 255   # RGB 像素值

    # 浮点类型
    var x: Float64 = 3.14159265358979
    var weight: Float32 = 0.001  # 神经网络权重
    var half: Float16 = 1.5    # 推理量化

    # 类型转换(显式,不允许隐式窄化转换)
    var f = Float64(a)          # Int → Float64
    var i = Int(x)              # Float64 → Int(截断小数部分)

    print(a, pixel, x, f, i)

字符串类型

Mojo 有两种字符串类型,区别在于内存所有权:

fn string_types():
    # StringLiteral:编译期字符串常量,存储在二进制文件中
    let greeting: StringLiteral = "Hello, Mojo!"

    # String:运行时字符串,堆分配,可以动态修改
    var message: String = String(greeting)  # StringLiteral → String
    message += " 🔥"    # 字符串拼接

    # 字符串方法
    print(message.upper())      # HELLO, MOJO! 🔥
    print(len(message))          # 15
    print(message.startswith("Hello"))  # True

struct 结构体

为什么用 struct 而不是 class?

Mojo 的 struct 与 Python 的 class 有本质区别。struct 的成员在内存中是固定布局的,没有动态派发开销,可以被放在栈上,是 Mojo 性能的基石。

# Python class
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    # 动态类型,堆分配
    # 可以随时添加属性
    # 有 __dict__ 字典开销
# Mojo struct
struct Point:
    var x: Float64
    var y: Float64
    # 固定内存布局(2 × 8 = 16 bytes)
    # 可以放在栈上
    # 无任何运行时字典开销

struct 完整示例

struct Vector2D:
    # 成员变量声明(固定类型)
    var x: Float64
    var y: Float64

    # 构造函数(__init__)
    fn __init__(inout self, x: Float64, y: Float64):
        self.x = x
        self.y = y

    # 方法
    fn length(self) -> Float64:
        return (self.x ** 2 + self.y ** 2) ** 0.5

    # 运算符重载
    fn __add__(self, other: Vector2D) -> Vector2D:
        return Vector2D(self.x + other.x, self.y + other.y)

    # __str__ 用于打印
    fn __str__(self) -> String:
        return "Vector2D(" + String(self.x) + ", " + String(self.y) + ")"

fn main():
    var v1 = Vector2D(3.0, 4.0)
    var v2 = Vector2D(1.0, 2.0)

    print(v1.length())   # 5.0
    var v3 = v1 + v2
    print(v3)            # Vector2D(4.0, 6.0)

alias:编译期常量

alias 的三种用途

# 1. 数值常量(类似 C 的 #define,但类型安全)
alias MAX_BATCH_SIZE: Int = 128
alias LEARNING_RATE: Float64 = 0.001
alias PI = 3.14159265358979

# 2. 类型别名(提高可读性)
alias F32 = Float32
alias Weight = Float32    # 语义化类型名
alias Logit = Float64

# 3. SIMD 类型别名(第5章详解)
alias F32x8 = SIMD[DType.float32, 8]   # 8个 Float32 的向量
alias I32x4 = SIMD[DType.int32, 4]     # 4个 Int32 的向量

fn use_aliases():
    var w: Weight = 0.5
    var v: F32x8 = 1.0    # 8 个 1.0 的向量
    print(w, MAX_BATCH_SIZE)
alias vs var/let 的区别

alias编译期常量,在编译时就被替换为其值,完全没有运行时开销。let 是运行时不可变变量,在运行时分配内存(尽管编译器可以优化掉)。对于性能敏感的数值常量(如向量宽度、批处理大小),alias 是最佳选择。

Optional[T] 可空类型

from utils.variant import Optional

fn find_item(items: List[Int], target: Int) -> Optional[Int]:
    for i in range(len(items)):
        if items[i] == target:
            return i    # 找到,返回索引
    return None           # 未找到

fn main():
    var nums = List[Int](10, 20, 30, 40)

    var result = find_item(nums, 30)
    if result:
        print("找到,索引为:", result.value())  # 2
    else:
        print("未找到")

    # 使用 or 提供默认值
    var idx = find_item(nums, 99)
    print(idx.value() if idx else -1)  # -1

实战:定义高性能矩阵结构

alias F32 = Float32

struct Matrix:
    """行优先存储的二维矩阵。"""
    var rows: Int
    var cols: Int
    var data: List[F32]

    fn __init__(inout self, rows: Int, cols: Int):
        self.rows = rows
        self.cols = cols
        self.data = List[F32](rows * cols)
        # 初始化为零
        for i in range(rows * cols):
            self.data[i] = 0.0

    fn __getitem__(self, row: Int, col: Int) -> F32:
        return self.data[row * self.cols + col]

    fn __setitem__(inout self, row: Int, col: Int, val: F32):
        self.data[row * self.cols + col] = val

    fn shape(self) -> String:
        return "(" + String(self.rows) + ", " + String(self.cols) + ")"

fn main():
    var m = Matrix(3, 4)
    m[0, 0] = 1.0
    m[1, 2] = 3.5
    print(m.shape())     # (3, 4)
    print(m[1, 2])       # 3.5
本章小结

var/letvar 可变,let 不可变(更多编译期优化);两者都有固定类型,不同于 Python 动态赋值。
数值类型:Int/Float64 与 Python 兼容;Int8/UInt8/Float32 等固定宽度类型用于 SIMD 和底层优化。
struct:固定内存布局,无动态字典开销,可以放栈上,是 Mojo 性能的基石;支持方法、运算符重载。
alias:编译期常量,用于数值、类型别名、SIMD 类型定义,零运行时开销。
Optional[T]:类型安全的可空值,消灭空指针异常。