Skip to content
当前页大纲

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°C

trait 之间也可以组合、细化(一个 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 对应
抽象基类 ABCtrait
isinstance 检查编译期 trait 约束(错误提前到编译期)
鸭子类型「能跑就行」trait 显式声明「我能跑」
多继承混入 Mixin多 trait 组合

经验法则:先写具体类型,等出现第二个需要相同能力的类型时再提炼 trait——和 Python 里「先写代码再抽基类」的节奏一致。

八、小结

  • trait = 能力契约;struct 用「符合 trait + 实现 dunder」获得能力
  • 泛型函数用 [T: SomeTrait] 声明约束,编译期专门化、零运行时开销
  • 常用 trait:Copyable / Movable / Stringable / Sized / KeyElement
  • 用 trait 组合取代继承体系

下一篇:所有权与值语义。

MIT License.