Mojo trait 特征系统
本篇覆盖:trait 定义与实现、泛型函数、标准库常用 trait。trait 是 Mojo 取代继承的组合机制,也是泛型编程的基础。
一、trait 是什么
trait 是一组方法要求的集合——「符合这个特征的类型,必须能做这些事」。struct 声明符合某个 trait,就必须实现它的全部要求。
概念上:
- 像 Java/C# 的 interface,但是编译期静态分发
- 像 Rust 的 trait,但语法声明更轻
- 不像 Python 的继承——没有字段继承,只有能力声明
二、定义与实现
定义 trait
mojo
trait Greeter:
def greet(self, name: String) -> String: ...方法签名后的 ... 表示「无实现,等待符合者实现」。
实现(符合)trait
mojo
@fieldwise_init
struct ChineseGreeter(Greeter):
def greet(self, name: String) -> String:
return t"你好, {name}!"
@fieldwise_init
struct EnglishGreeter(Greeter):
def greet(self, name: String) -> String:
return t"Hello, {name}!"三、用 trait 写泛型函数
trait 真正的威力:函数不再绑定具体类型,而是接受「任何符合 trait 的类型」。注意编译期参数写在中括号里:
mojo
def welcome[T: Greeter](g: T, name: String):
print(g.greet(name))
def main():
welcome(ChineseGreeter(), "莫伟") # 你好, 莫伟!
welcome(EnglishGreeter(), "Mojo") # Hello, Mojo![T: Greeter] 是编译期参数(第七篇详解)——编译器会为每个实际用到的 T 生成一份专门化的机器码,没有虚函数表、没有运行时开销。这是「泛型的易用 + 静态的性能」同时拿到的关键。
四、标准库常用 trait
写 struct 时最常打交道的几个:
| Trait | 要求 | 作用 |
|---|---|---|
Copyable | 拷贝构造 | 允许值拷贝(放进 List 大多需要它) |
Movable | 移动构造 | 允许所有权转移(容器存储的最低要求) |
Stringable | __str__ | print() / 字符串化可用 |
Intable | __int__ | 可转 Int |
Sized | __len__ | 支持 len() |
Collection | __len__ 等 | 集合类类型 |
KeyElement | 哈希+相等 | 可作 Dict 的键 |
实践原则:
- 默认给 struct 加
(Copyable),除非有明确理由禁止拷贝(如独占资源句柄) - 想被
print就加Stringable并实现__str__ - 想当 Dict 键就实现
KeyElement的要求
五、trait 组合
一个 struct 可以同时符合多个 trait(取代多继承):
mojo
@fieldwise_init
struct Temperature(Copyable, Stringable):
var celsius: Float64
def __str__(self) -> String:
return t"{self.celsius}°C"
def main():
var t = Temperature(25.5)
print(t) # 25.5°Ctrait 之间也可以组合、细化(一个 trait 要求符合另一个 trait),标准库内部大量使用这种机制。
六、trait 约束报错长什么样
不符合 trait 却被使用时,编译器直接拦截:
mojo
def welcome[T: Greeter](g: T, name: String):
print(g.greet(name))
def main():
welcome(42, "x") # 编译错误: Int 不符合 Greeter错误在编译期而非运行期暴露——这就是静态类型系统在泛型场景下的价值。
七、设计建议
从 Python 迁移设计时的心智转换:
| Python 习惯 | Mojo 对应 |
|---|---|
| 抽象基类 ABC | trait |
isinstance 检查 | 编译期 trait 约束(错误提前到编译期) |
| 鸭子类型「能跑就行」 | trait 显式声明「我能跑」 |
| 多继承混入 Mixin | 多 trait 组合 |
经验法则:先写具体类型,等出现第二个需要相同能力的类型时再提炼 trait——和 Python 里「先写代码再抽基类」的节奏一致。
八、小结
- trait = 能力契约;struct 用「符合 trait + 实现 dunder」获得能力
- 泛型函数用
[T: SomeTrait]声明约束,编译期专门化、零运行时开销 - 常用 trait:
Copyable/Movable/Stringable/Sized/KeyElement - 用 trait 组合取代继承体系
下一篇:所有权与值语义。