知乎链接:zhuanlan.zhihu.com/p/518978071
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
学 Rust 是个很流行的话题,Rust 是这些年,真正把一系列 modern 的特性融合在自己身上的一门语言。抛开流行,确实能在 Rust 里学到很多东西。并且由于 Rust 和 js/ts 的相似性,实际上一个资深的 ts 开发者学习 Rust,可能都会对 Rust 的某一部分特性惊呼,这不就是我想要的功能么。。
实际上已经有非常多 from js to Rust 的教程:
Rust Tutorial: An Introduction to Rust for JavaScript Devs - SitePoint www.sitepoint.com/rust-tutori…
24 days from node.js to Rust vino.dev/blog/node-t…
还有很多。
如果只是 Rust 本身实战性质的书和文章就更多了。我的生产语言是 TypeScript,我的出发点,更多的是一个 TyperScripter 的视角去看 Rust,以及 Rust 如何更好的服务好 TS。
这个系列,我的计划是把和 JS/TS 明显不同的部分,以 topic 的形式,逐个击破。例如,宏,目前是完成了官网的文档的翻译,就目前我对宏的理解,最主要还有各个宏,更细节的实例,以及 syn、quote、proc_macro2 的实例。宏观的文章很多,寻找优秀的进行翻译,以及加上一些个人认为对 JS/TS 开发者有用的注解。但是,细节的文章,好的就不多了。例如 24 days from node.js to Rust。我在学习这个教程的时候,就明显感觉,难度呼呼就上去了,后面一大部分,更像是索引。
但是,这些教程的作者有一个观点,我也很认同,因为大家是从 js/ts 的角度去学习 Rust,完全可以先忽略更细粒度的开销性质的特性,完全可以把 Rust 当成一个 better java、better go、faster ts(个人认为,ts 和 Rust 的编译器属于一样好,一样 modern 的编译器,纯个人认为,Rust 的编译器缺少 ts 的可编辑性,这一点 ts 更优秀,ts 可以把自己设置为爱标注不标注的松散 js 的状态。当然,毕竟 ts/js 是两门语言) 的东西。生命周期弄不明白,你就 clone 一个呗。所以,这个系列的文章,更多的是关注,js/ts 开发者如何能最短时间,最快速度,用 Rust 写一个能上生产的程序,而不是 js/ts 如何变成一个 c/c++ 程序员,对着内存跳舞的状态,个人认为,这样意义也是不大的。
另一方面,Rust 并不是一个只能进行系统开发的语言,它其实有非常功效的一面,使用 crates.io 的包快速开发软件,是可行的。
回到这个系列,我目前会优先选择的话题如下,这一部分的特点是,和 js/ts 的差别非常大,这一部分首先重要的是原理,其次是实例:
- 宏
- ownership
- packages、crates、modules
- lifetimes
- smart pointers
- concurrency
个人认为,对于 js/ts 开发者比较简单的话题如下,这一部分,更重要的不是原理,而是实例,这一部分我的计划是找场景,和优秀的库的样例代码:
- trait
- 实际上,在 ts 里很容易可以实现类似 trait 的范式。
- 因为只要写 Rust,不可能不写 trait,这一块通过时间和项目,你总能学会。
- iterators
- 主要是语法的区别,不考虑细节的话,使用起来差别不大。
- 这块的核心关键,应该是多用。有一些库,类似 lodash。
- 闭包
- 和 js/ts 有区别,但是在单 cpu 场景下区别有限
- 异步
- 在单 cpu 场景下区别有限,每个实现,要去研究那个实现的特性,比如使用 tokio 的代码,一般在 main 里会有个 setup_exit_process_panic_hook。这种只能是去研究那个 crate,以及官方的 trait
- 工具链
- 比 js 好用太多,如果你能搞定一个 pnpm 的 monorepo,并且集成 eslint、prettier、等等工具。我相信你肯定能搞定 Rust 的工具链。
- 可以把 js 的工具链带到 Rust,比如 turborepo 可以通过 package.json 来调用
- monorepo
- Rust 的项目大都是 monorepo 的,本身对 monorepo 支持也不错。但是 和 ts 的 monorepo 有比较大的差别。这一点我也还在摸索。但是使用上,规模不大时,区别不大。
- 个人理解,Rust 和 ts 混合的项目,可以充分利用 Cargo.toml 和 package.json ,进行项目管理
- 其他基础语法
- 例如循环,更多是语法的区别。
对有函数式编程经验的 js/ts 开发者比较简单的话题是,这一部分,最主要的是函数式,代码每一个复杂的,问题是,为啥要这么做,而为啥要这么做,是最难写的:
- pattern matching、iterators、error handling
- 这一块孤立的学是意义不大的,如果你有函数式编程的经验,你会觉得这一部分很简单
- error handling 更重要的是你选择的 error handling 的框架,或者你自己设计的 Rust 里的 error handling 的机制
- error handling、pattern matching 两者几乎是同时使用的
- 最好对函数能有可能报错,和不可能报错的区分的概念,比如在函数式编程里 task 的概念,就是一个可能失败的函数,这个可能失败的函数,会把失败要留下的信息,放在 error 里,这个 error 要填哪些信息,你需要声明在你的 error handling 的框架里,然后使用就好了。
- 这部分的难度,个人认为是开发者对函数式编程的理解,和 Rust 没啥关系,实际上如果你在自己的 js/ts 里也有一套错误处理机制,尤其是使用了类似 fp-ts 的库,和 rust 实现没有任何本质区别。
- Rust 的一大安全型来源在于,要求开发者自己把可能出现的问题标注出来,错误就是这一部分。
- 这一部分虽然放在这里,但是在看源码时,实际是特别难的一块,因为稍微的规模的 Rust 软件,错误可能就几百几千个。
- 建议在 ts 里使用 ts-pattern,可以提前演练下这种思维
对有范型经验的 ts 开发者比较简单的话题是,这一部分,其实都让库作者/架构师消化掉了,单纯的文章,更像是索引,讲之间的逻辑关系,和数学差不多的。这一部分的实战,更多是为了把两个/或者多个领域的代码连起来,基本每个项目都有自己的出发点。但是,这个问题不是完全无迹可寻的,比如 prisma 生成的类型,就是一个非常好的实例,prisma 相当于就是定义了一套 dsl,然后用户写好 dsl 以后,prisma 通过他自己的宏机制,生成了一大堆代码和类型。另外就是 open-api,通过范型解出来类型安全的函数调用。个人认为,这一块做的最好的是 prisma,研究 prisma 一定有所收获:
- 范型
- 范型的难点,从来不是语法。相信在生产大量使用范型的童鞋也许会赞同这一点。
- Rust 因为有宏,宏和范型结合,有了相当多的可能性,同时带来了更无法用语言描述的复杂度。