相同方法不同状态下在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. 会看到 pay 和 ship,容易误点 智能筛选 如果是新订单,敲 order. 根本看不到 ship
所有权 状态可变 对象一直是那个对象,只是改了个字段值 旧状态销毁 调用 pay() 后,旧的 order 变量直接失效 (Move),防止你再用旧订单搞事情

Export to Sheets

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

相关推荐
代码方舟几秒前
零信任架构实战:基于天远人企关联构建自动化供应链核心企业关联穿透与利益冲突审查网关
java·人工智能·架构·自动化
奈落2411 分钟前
Dify 管对话、n8n 管执行:把‘大脑+手脚‘的组合架构对照三方平台资料读清楚
人工智能·架构·agent·ai 办公
qianbo_insist25 分钟前
# 在 Qwen3.5-2B 上把 GDN-1 无损换成 GDN-2:一次完整的架构手术实验(负结果)
人工智能·pytorch·python·架构
4SAPI3 小时前
多模态 AI 应用统一接入网关生产选型指南:4SAPI、硅基流动与 OpenRouter 的架构权衡
大数据·人工智能·架构·php·1024程序员节
微订校园外卖平台4 小时前
县城多门店外卖履约系统怎么做:统一后台、共享骑手池与分店对账的架构清单
大数据·架构·外卖系统·本地生活·微订
4SAPI4 小时前
营销内容生成大模型 API 生产选型指南:4SAPI、硅基流动与 OpenRouter 的架构权衡
数据库·人工智能·架构·php
硅基手札4 小时前
操作系统设计思想系列 13 · NuttX 深度 — POSIX 飞控底座 + RTOS 群像
架构·操作系统·嵌入式开发·ecos·sel4·nuttx
海宇服务5 小时前
零信任架构实战:基于海宇个人大数据报告构建自动化汽车融资租赁资信核验网关
大数据·人工智能·架构·自动化
杨云龙UP5 小时前
生产实战:Keepalived + Nginx + MySQL 双向复制高可用架构部署与原理详解
运维·数据库·mysql·nginx·架构·keepalived·高可用架构
数字营销观察5 小时前
geo管理软件选型指南:从技术架构到场景落地的核心考量
架构