最近突然又有了干劲,拿出时间学习了一下 Rust. 之前也尝试过学习,止步于简单看看官方的那本书,这次的区别在于尝试写了一些代码, 包括到 LeetCode 和 Project Euler 做了些简单的题,以及最近正好自身有需求写了个命令行小工具。总的来说 Rust 给我感受是如果恰好你要用的库都维护地很好,那对于刷点题(链表除外),做点小东西来说写起来真的是挺爽的。如果自己造轮子,就要涉及些更高级的特性,我现在能力还不是很够,就比较费劲了。
在这里把对于 Rust 一些浅薄的想法还有学到的一些经验从各个角度记录一下,不一定对,欢迎纠错和讨论。
语言特性
语言特性方面,我现在对 Rust
的印象就是 C++
内存管理那套加上 Haskell
的一些函数式的东西以及 Ruby
的部分语法。
第一次尝试学 Rust
是本科的时候,看到结构体和枚举类那里就歇菜了。“这是啥?结构体还能就一个名字?枚举还能附加好几个类型?”
然后后面因为觉得自己从来没接触过函数式语言就去看了看号称最 pure 的 Haskell
(但还是不会写,只是略微搞懂了些概念),
回头再看 Rust
的一些概念就恍然大悟了,不仅是前面说的枚举类,还有比如 Option
, Result
, 迭代器啥的,就很眼熟。
我感觉最近的新一些的编程语言基本都带上一些函数式语言的特性,别的不说,模式匹配、闭包啥的都快成各种语言的标配了。 找一门函数式语言学一学基本的概念还是挺有用的。
到了内存管理这块,我本身 C++
学得也不好,智能指针之类的都没碰过,拷贝构造赋值构造啥的也学得很混乱,经常搞出段错误,
所以学 Rust 的时候也受到些影响,经常引用来引用去的就蒙了,尤其是有闭包出现的场景。这块能力不够是比较影响写代码爽度的。
包管理
包管理这体验也不错,好像新一些的语言都会带一个官方自己的包管理器,不像 C/C++
有不知道究竟多少种构建软件和构建方式。
项目目录的管理上也挺好接受的。
但是编译确实挺慢的,我写的这个几百行小项目,完全从头编译要 CPU 全满跑上三分钟,而且整个项目目录编译完占的空间也比较大。
标准库
标准库还是带了很多常用方法的,比如Vec
还带了个windows(n)
方法自动分割成定长的滑动窗口,不像C++
连个string
的split
都无。
我用的比较多的是Iterator
, 这里也带了很多函数式的东西,map
,flat_map
,fold
,scan
啥的。
Go vs Rust
虽然这俩语言并不是在一个生态位上,但如果非得从 Go
和 Rust
选一个,我估计会选 Rust
. 虽然 Go
挺好上手的,比较劝退我的是变量命名规范、国内网络环境的特有问题和异常处理。
说到变量命名,之前本科的时候可能没太注意,但我现在发现我可能对于官方推荐驼峰命名变量和函数的语言有种天然的排斥。我还是更喜欢变量和方法名全小写加下划线,读起来更容易些。
说到国内网络环境问题,虽然有代理,但Go
写出来全是静态编译的,我最常用的Proxychains
用不了了,有点不方便。而且代码里可以直接用GitHub
路径引用别人包的方式我也不太喜欢。
说到异常处理,写C/C++
的时候我都是瞎搞的,也没按规范来,随便返回个数。写python
的时候try
就经常要缩进看起来很繁琐。Go
我写得不多,写的时候也很少处理错误哈哈。
而Rust
的这个把数据包起来的方案挺函数式的。在看Haskell
的时候Maybe Monad
都是被各个教材反复强调的,印象挺深刻。
所以这里学得时候也很熟悉,而且那个?
的语法糖确实挺香的。当然,我现在用的还很初级,基本都是返回现成的错误,不知道项目复杂后想法会不会改变。
async/await
Rust
的协程我还没系统学,现在也还没搞清楚tokio
和futures
是个啥关系。
写这个小项目的时候刚好有一个需求是类似现在的 pacman 并行下载,
“在一个普通函数里同时开启数个协程下载内容并按顺序返回数据,要求限制最大并发数”,
很简单的需求,也不涉及互相通信,却搞了我好久。
主要是我看文档始终没有搞清楚futures
里面stream
的用法,网上搜例子也不多。
作为记录,贴一下最后完成的代码的大体结构
fn function() {
const CONCURRENT_NUM: usize = 5;
let rt = tokio::runtime::Runtime::new().unwrap();
let results = rt.block_on(async {
/* x 内有个 async 方法 do_sth() 访问网络并修改自身的数据 */
let tasks = xs.iter_mut().map(|x| x.do_sth());
stream::iter(tasks)
.map(|task| async { task.await })
.buffered(CONCURRENT_NUM)
.collect::<Vec<_>>()
.await
});
handle_results();
}
总之这块的生态我觉得可能还不够成熟,我等一段时间再学习下。
静态编译
虽然不是默认静态编译,但也是支持的。具体操作方法(只看 linux
):
- 添加
musl
的target
rustup target add x86_64-unknown-linux-musl
- 如果用到
openssl
要在Cargo.toml
里添加openssl = {version = "0.10", features = ["vendored"]}
- 构建时指明
target
cargo build --target x86_64-unknown-linux-musl
- 编译好后可以用
file
或ldd
命令检查下
musl
是一个C标准库实现,至于要不要安装musl
我不记得了,反正我装了。感觉构建时要编译openssl
应该要用到吧。
不喜欢的地方
- 要先去
crates.io
找库,看怎么引入,再去docs.rs
看文档,而且文档都是自动生成,经常只给出了一堆定义, 我这样的新手有点难看懂,不知道怎么用。 - 感觉生态不完善,缺少很多常用并且有人持续维护的大型库。
- 用的人还不够多,网上找资料有时候不好找或过时,有种之前搞
vpp
时经历的难受感,真的很劝退。 - 编译速度太慢了。
总结
总的来说我还挺喜欢的,但我还没接触太深,可能只尝到了浅层的一些好处。目前来看,对我来说可以当作兴趣去学,要靠这个吃饭还不太可能。我应该会持续关注,希望未来能越来越好用,越来越大众化吧。Mozilla
给点力啊,Servo
啥时候出?