变量声明:var、let 与 Python def
三种声明方式对比
Mojo 引入了两个新关键字 var 和 let,与 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
布尔类型,值为
True 或 False。与 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/let:var 可变,let 不可变(更多编译期优化);两者都有固定类型,不同于 Python 动态赋值。
数值类型:Int/Float64 与 Python 兼容;Int8/UInt8/Float32 等固定宽度类型用于 SIMD 和底层优化。
struct:固定内存布局,无动态字典开销,可以放栈上,是 Mojo 性能的基石;支持方法、运算符重载。
alias:编译期常量,用于数值、类型别名、SIMD 类型定义,零运行时开销。
Optional[T]:类型安全的可空值,消灭空指针异常。