01 学习路线与心智模型(从 JS/TS/Java 到 Rust)

本章目标

  • 建立一套可落地的 Rust 学习路线(不是“看完语法”,而是“能独立改项目”)。
  • 用你熟悉的 JS/TS/Java 心智模型对照理解:Rust 到底“难”在哪里、值得在哪里。
  • 结合 asset-light 这类“带 UI + 本地 DB”的项目,明确你要掌握哪些核心能力。

Rust 与你熟悉语言的差异(快速对照)

  • 内存管理方式
    • JS/TS/Java:GC(垃圾回收)+ 运行时开销可控但不可见
    • Rust:编译期所有权(Ownership)+ 零成本抽象(把“内存规则”变成编译器校验)
  • 错误处理
    • JS:throw / try-catch(运行时)
    • Java:checked/unchecked exception(类型系统里有一部分)
    • RustResult<T, E> / Option<T>(错误与缺失值默认都是“类型的一部分”)
  • 并发与共享
    • JS:单线程事件循环 + message passing(Web Worker 等)
    • Java:多线程 + 锁/并发结构
    • Rust:多线程 + 借用规则 + Send/Sync(类型系统约束并发安全)
  • 抽象方式
    • TS:结构类型 + 泛型 + union
    • Java:名义类型 + interface/class + 泛型
    • Rust:trait + 泛型 + enum(表达力很强,代价是需要更多“类型思维”)

你需要建立的 3 个 Rust 心智模型

1) “值 = 资源”,所有权是默认规则

在 Rust 里,StringVec<T>、数据库连接、文件句柄都可以视为“资源”。
资源默认只能有一个拥有者,当拥有者离开作用域,资源就被释放。

你会经常遇到“move(移动语义)”:

  • 把一个 String 赋给另一个变量,默认是移动,旧变量不能再用。
  • 想继续用?你要么传引用(借用),要么显式 clone()(付出复制成本)。

2) “类型 = 约束”,把不变量写进类型

Rust 鼓励把业务不变量写进类型与 API:

  • Option<T>:可能为空(不再用 null/undefined 兜底)
  • Result<T, E>:可能失败(错误必须被处理或传播)
  • enum:用“可枚举的状态集合”替代“字符串魔法值”

asset-light 里,分类体系就体现得很强:

  • Category:资产大类(稳定枚举 + 稳定编码)
  • AssetScope:口径(投组 vs 净资产项)
  • VehicleType:工具类型(载体维度)
  • AssetSubCategory:子资产类别(稳定编码)

对应文件:src/models/category.rssrc/models/asset_scope.rssrc/models/vehicle_type.rssrc/models/asset_sub_category.rs

3) “编译器 = 教练”,让报错成为学习反馈

Rust 的学习体验非常像“被严格的教练带着做题”:

  • 你写的代码不符合规则,编译器会阻止你继续
  • 你一开始会觉得慢,但当你熟悉模式后,bug 会大幅减少。

建议把 Rust 报错当作“反馈提示”而不是“障碍”:

  • 先读第一段总结(通常就告诉你核心矛盾)
  • 再看它标出的两处代码位置(borrow 的来源与冲突点)
  • 最后再决定:借用?clone?重构 API?

结合 asset-light 的学习路线(推荐顺序)

这份指南不是按语法目录写的,而是按“能把项目改起来”写的。

第 0 步:先跑起来(30 分钟)

  • 运行入口:src/main.rs
  • 根组件:src/app.rs
  • 路由:src/router.rs
  • 数据库初始化:src/db/mod.rssrc/db/connection.rs

你要能回答:

  • 应用从哪里启动?
  • DB 什么时候初始化?DB 路径在哪里?
  • 页面切换是谁控制的?

第 1 步:读懂模块结构(1-2 小时)

先把“文件夹就是模块”的结构读顺:

  • src/models/*(领域模型)
  • src/db/*(Repository + 连接)
  • src/pages/*(页面)
  • src/components/*(UI 组件)
  • src/state/*(全局状态)

第 2 步:啃下所有权/借用(2-5 小时,分多次)

这是 Rust 的核心门槛,也是一旦跨过就能持续受益的部分。
建议配合本项目里这两个“真实场景”来学:

  • Signal<AppState>.read() / .write()(UI 状态读写)
  • Mutex<Connection> + OnceLock<Database>(全局 DB 单例 + 可变共享)

第 3 步:掌握 DB 访问与类型转换(2-4 小时)

从“能读能写”入手,再讨论“优雅与健壮”:

  • AssetRepositoryinsert/update/find_all
  • SnapshotRepositorycreate/find_all
  • PlanRepositorycreate/update/set_active

对应目录:src/db/*

第 4 步:理解 Dioxus 的组件与数据流(2-4 小时)

你要能熟练定位:

  • 页面:src/pages/*
  • 组件:src/components/*
  • 路由/导航:src/router.rssrc/components/layout/sidebar.rs

本章练习(不改业务逻辑,先建立“地图”)

练习 A:从入口走一遍执行链

  1. 打开 src/main.rs,找到:
    • db::init_database()
    • dioxus::launch(app::App)
  2. 打开 src/db/mod.rs,确认:
    • OnceLock<Database> 是怎么实现“全局单例”的
  3. 打开 src/router.rs,确认:
    • Route 枚举定义了哪些页面
  4. 打开 src/components/layout/sidebar.rs,确认:
    • Sidebar 导航项和 Route 的对应关系

练习 B:找到 DB 路径逻辑

打开 src/db/connection.rsDatabase::db_path(),回答:

  • 默认 DB 文件落在哪里?
  • 你如何通过环境变量指定 DB 路径?(提示:ASSET_LIGHT_DB_PATH