相同方法不同状态下在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),但在使用时是多么的安全和省心。

相关推荐
Tadas-Gao21 小时前
AI是否存在“系统一”与“系统二”?——从认知科学到深度学习架构的跨学科解读
人工智能·架构·系统架构·大模型·llm
程序员JerrySUN1 天前
OP-TEE + YOLOv8:从“加密权重”到“内存中解密并推理”的完整实战记录
android·java·开发语言·redis·yolo·架构
ZStack开发者社区1 天前
替代VMware VCF | 详解ZStack Cloud开放架构与异构整合能力
架构
小股虫1 天前
分布式事务:在增长中台,我们如何做到“发出去的内容”和“记录的数据”不打架?
分布式·微服务·云原生·架构·团队建设·方法论
乾元1 天前
数据中心流量工程(TE)优化:当 AI 成为解决“维度诅咒”的唯一操纵杆
运维·服务器·网络·人工智能·架构·自动化
云器科技1 天前
NinjaVan x 云器Lakehouse: 从传统自建Spark架构升级到新一代湖仓架构
大数据·ai·架构·spark·湖仓平台
用户91743965391 天前
从单系统架构到微服务架构:软件现代化的转型综述
微服务·架构·系统架构
断春风1 天前
从 JDK 8 到 JDK 21:企业级 Java 版本选择的架构思考
java·架构·jdk
h7ml1 天前
构建可扩展的企业微信消息推送服务:事件驱动架构在Java中的应用*
java·架构·企业微信
墨辰JC1 天前
STM32架构基于调度器的非阻塞按键状态机设计
stm32·microsoft·架构·状态机·调度器