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

相关推荐
泉城老铁2 小时前
springboot+redis 如何实现订单的过期
java·后端·架构
cetcht88883 小时前
配电房 AI 巡检机器人系统:技术架构、核心功能与工程实现全解析
人工智能·架构·机器人
Jerry952706284 小时前
1.无服务器架构入门
云原生·架构·serverless·无服务器架构
切糕师学AI5 小时前
ARM 架构中的 PRIMASK、FAULTMAST、BASEPRI 寄存器
arm开发·架构·嵌入式·寄存器
听风吟丶5 小时前
微服务分布式事务实战:从数据一致性到故障恢复全方案
分布式·微服务·架构
子春一5 小时前
Flutter 2025 测试策略全景:从单元测试到混沌工程,构建坚不可摧的高质量应用
flutter·架构
timmy-uav6 小时前
BetaFlight代码解析(20)—屏幕显示(OSD)
架构·系统架构·无人机·飞控·betaflight
神算大模型APi--天枢6466 小时前
国产硬件架构算力平台:破解大模型本地化部署难题,标准化端口加速企业 AI 落地
大数据·前端·人工智能·架构·硬件架构
武子康6 小时前
Java-192 深入拆解 EVCache 内部原理:Memcached 架构、Slab 分配与 LRU 过期机制全解析
数据库·redis·缓存·架构·memcached·guava·evcache