相同方法不同状态下在ts和rust的写法(是我们一直追求的编译阶段)

先写个ts,代码让我明白逻辑,然后写rust通过枚举写出和ts一样的运行时代码,然后再写rust泛型的编译判断

我们将模拟同一个业务场景:订单系统 。 流程:新建 (New) -> 支付 (pay) -> 已支付 (Paid) -> 发货 (ship) -> 已发货 (Shipped)


1. TypeScript 写法 (经典 OOP,运行时检查)

这是大多数程序员最熟悉的写法。状态是一个字符串或枚举值,逻辑全靠 if 判断。

TypeScript

typescript 复制代码
// 定义状态类型
type OrderStatus = 'New' | 'Paid' | 'Shipped';

class Order {
    id: number;
    status: OrderStatus; // 状态只是一个数据字段

    constructor(id: number) {
        this.id = id;
        this.status = 'New'; // 默认为 New
    }

    // 支付方法
    pay() {
        // 【痛点】:必须在运行时写代码检查当前状态
        if (this.status !== 'New') {
            throw new Error(`错误:订单 ${this.id} 状态是 ${this.status},不能支付!`);
        }

        console.log(`订单 ${this.id} 支付成功`);
        this.status = 'Paid'; // 修改内部状态
    }

    // 发货方法
    ship() {
        // 【痛点】:如果你忘了写这个 check,或者写错了,编译时不会报错,运行时会炸
        if (this.status !== 'Paid') {
            throw new Error(`错误:订单 ${this.id} 没支付,不能发货!`);
        }

        console.log(`订单 ${this.id} 发货成功`);
        this.status = 'Shipped';
    }
}

// --- 测试 ---
const order = new Order(1);

// 1. 正常流程
order.pay();
order.ship();

// 2. 错误流程 (编译器拦不住,运行才会报错)
// order.ship(); // 抛出 Error: 错误:订单 1 状态是 Shipped,不能发货!

2. Rust 枚举写法 (模拟 TS 逻辑,运行时检查)

这是 Rust 中处理动态状态的标准写法。逻辑和 TS 几乎一样,只是用 match 替代了 if,用 Result 替代了 throw Error

特点: 代码写在同一个 impl 里,可以编译通过非法调用的代码(比如连续调用两次 pay),必须运行起来才知道错没错。

Rust

rust 复制代码
#[derive(Debug, PartialEq)]
enum Status {
    New,
    Paid,
    Shipped,
}

#[derive(Debug)]
struct Order {
    id: u64,
    status: Status, // 状态是结构体的一个字段
}

impl Order {
    fn new(id: u64) -> Self {
        Self {
            id,
            status: Status::New,
        }
    }

    // 必须返回 Result,因为运行时可能会失败
    fn pay(&mut self) -> Result<(), String> {
        // 【运行时检查】:和 TS 一样,必须手动判断状态
        match self.status {
            Status::New => {
                println!("订单 {} 支付成功", self.id);
                self.status = Status::Paid; // 修改自身字段
                Ok(())
            }
            _ => Err("只有新订单才能支付".to_string()),
        }
    }

    fn ship(&mut self) -> Result<(), String> {
        // 【运行时检查】
        match self.status {
            Status::Paid => {
                println!("订单 {} 发货成功", self.id);
                self.status = Status::Shipped;
                Ok(())
            }
            _ => Err("只有已支付的订单才能发货".to_string()),
        }
    }
}

fn main() {
    let mut order = Order::new(1001);

    // 1. 正常流程 (需要处理 Result)
    if let Ok(_) = order.pay() {
        let _ = order.ship();
    }

    // 2. 错误流程
    // 编译器完全允许你写这行代码,只有跑起来通过 Err 告诉你错了
    // let _ = order.ship(); // 运行时返回 Err
}

3. Rust 泛型 + PhantomData (Type State,编译时检查)

这是 Rust 的"完全体"。状态不再是字段里的 ,而是依附在结构体上的类型

特点: 方法被拆分到不同的 impl 块。非法调用的代码连编译都过不去

Rust

rust 复制代码
use std::marker::PhantomData;

// --- 1. 定义状态 (空结构体,只做类型标签) ---
struct New;
struct Paid;
struct Shipped;

// --- 2. 定义核心结构体 (带有泛型 State) ---
// 注意:这里没有 `status` 字段了!状态由 State 类型本身决定
struct Order<State> {
    id: u64,
    // 这是一个 0 大小的字段,只为了告诉编译器 State 是啥
    _marker: PhantomData<State>, 
}

// --- 3. 针对不同状态实现不同的方法 ---

// 只有 <New> 状态才有 pay 方法
impl Order<New> {
    fn new(id: u64) -> Self {
        Self { id, _marker: PhantomData }
    }

    // 动作:New -> Paid
    // 注意:输入是 self (消耗掉旧订单),输出是 Order<Paid> (返回新订单)
    fn pay(self) -> Order<Paid> {
        println!("订单 {} 支付成功", self.id);
        Order { 
            id: self.id, 
            _marker: PhantomData 
        }
    }
}

// 只有 <Paid> 状态才有 ship 方法
impl Order<Paid> {
    // 动作:Paid -> Shipped
    fn ship(self) -> Order<Shipped> {
        println!("订单 {} 发货成功", self.id);
        Order { 
            id: self.id, 
            _marker: PhantomData 
        }
    }
}

// 只有 <Shipped> 状态才有 finish 方法
impl Order<Shipped> {
    fn finish(self) {
        println!("订单 {} 流程结束", self.id);
    }
}

fn main() {
    // --- 1. 正常流程 ---
    let order_new = Order::<New>::new(1001); // 类型: Order<New>
    let order_paid = order_new.pay();        // 类型: Order<Paid>
    let order_shipped = order_paid.ship();   // 类型: Order<Shipped>
    order_shipped.finish();

    // --- 2. 错误流程 (高能预警) ---
    
    // 如果你试图把上面的流程打乱,比如直接对 New 状态发货:
    /* let order = Order::<New>::new(1002);
       order.ship(); 
    */
    
    // 💥 编译器直接报错:
    // error[E0599]: no method named `ship` found for struct `Order<New>`
    // 意思:兄弟,Order<New> 这个类型根本就没有 ship 这个函数,你想啥呢?
}

终极对比总结

维度 TS / Rust Enum 写法 Rust 泛型 (Type State) 写法
状态本质 数据 (Data) 存储在内存的一个变量里 ("New", 0) 类型 (Type) 写在代码签名里 (<New>, <Paid>)
逻辑位置 集中在一起 所有方法都在一个 class/impl 里,内部用 if/match 分流 分散隔离 pay 只存在于 New 的实现块里 ship 只存在于 Paid 的实现块里
错误发现 运行时 (Runtime) 跑起来抛异常或返回 Err 编译时 (Compile Time) 代码还没跑,编译器就标红了
IDE 体验 全显示order. 会看到 payship,容易误点 智能筛选 如果是新订单,敲 order. 根本看不到 ship
所有权 状态可变 对象一直是那个对象,只是改了个字段值 旧状态销毁 调用 pay() 后,旧的 order 变量直接失效 (Move),防止你再用旧订单搞事情

Export to Sheets

通过这三段代码,你应该能明显感觉到:Rust 的泛型写法虽然定义起来稍微啰嗦(要写好几个 struct 和 impl),但在使用时是多么的安全和省心。

相关推荐
jinanwuhuaguo1 小时前
(第二十七篇)OpenClaw四月的演化风暴:OpenClaw 2026年4月全版本更新的文明级解读
大数据·人工智能·架构·kotlin·openclaw
James_WangA1 小时前
我给 AOI 设备装了一个 Agent,然后发现工具注册才是最难写的
架构·github
James_WangA1 小时前
产线上跑 Agent:LLM 挂了不是 500 错误,是停线
架构·github
生成论实验室2 小时前
《事件关系阴阳博弈动力学:识势应势之道》第四篇:降U动力学——认知确定度的自驱演化
人工智能·科技·神经网络·算法·架构
SamDeepThinking2 小时前
并发量就算只有2,该上锁还得上呀
java·后端·架构
Sam_Deep_Thinking2 小时前
如何让订单系统和营销系统解耦
java·架构·系统架构
ting94520003 小时前
Micro1 超详细深度解析:架构原理、部署实战、性能评测与落地应用全指南
人工智能·架构
该昵称用户已存在3 小时前
从边缘计量到碳足迹追踪:MyEMS 开源一体化架构的全栈拆解
架构·开源
福大大架构师每日一题4 小时前
ollama v0.22.1 重大更新全解析:新增Poolside集成、模型推荐机制与多架构适配
架构·ollama
该昵称用户已存在4 小时前
以开源筑基,架构先行——深度拆解 MyEMS 微服务能源管理系统的技术内核
微服务·架构·开源