Rust小试牛刀
Rust的书已经看完2本了,看程序基本上能看懂一些了(生命周期这块还是迷迷糊糊),但是觉得多少还是要自己写一下程序,实际应用一下才能学会。正好今天找了一个练手的小项目,初步尝试一下
背景
N年之前给媳妇写过一个小程序,处理Excel数据的,大致就是从A.xlsx读取数据,分组合并并计算,然后追加写入到B.xlsx,但是其中有个字典关系的映射,以前是维护在代码中的,每次修改字典都要重新编译。
加上当时程序是.Net写的,WinForm程序,依赖Windows和VisualStudio这个组合,最近今年工作基本就没用过Windows,电脑也换成了M1的Mac,给她处理这个程序要安装个arm版Windows不说,还要安装VisualStudio,本就不富裕的硬盘,又被拖走了几十个G。
想着既然学了Rust,加上为了处理这个程序,索性就用Rust重写一份吧。
准备工作
- Rust的安装就不多说什么了,按照官网的脚本执行一下就搞定了
- 编辑器我用的是VisualCode,配合rust-analyzer插件,debug就是直接用CodeLLDB。
- 因为要解析xlsx文件,在
https://lib.rs/search?q=xlsx
查了一圈,最后决定用umya-spreadsheet="0.8.3"
- 因为有个需要配置的字典,考虑json和toml,最后决定用toml,因为对我媳妇而已,这个更直接一些,最终选择
toml="0.5.0"
开工
cargo new一个项目,添加好依赖,开整,只是逻辑的话,其实很简单,按照.Net的逻辑直接迁移过来就行了,不过有一说一,写的过程中还是挺多麻烦的,光看书以为自己掌握的东西,coding的时候却干着急想不起来怎么写,最后还是依赖搜索引擎解决。
另外还有一点就是关于mod,直到程序写完了,也没太get这个玩意和目录的关系怎么搞。所以最终的项目还是基于一个main.rs实现的。
逻辑啥的,就不具体写这里了,记录一下自己开发过程中记得住记不住的东西吧。
结构体
读取数据之后肯定不能直接用个元祖或者Map传来传去,怎么也得有个结构体来组装数据,为了隔离开,我这里还是用mod来进行一下简单的区分。
1 | pub mod model { |
别看就上面几个字段,就这个type_
还折腾我一会,因为type是关键字,如果确实要用type做这个字段名称,就要用r#type
, 还有就是从mod到struce到field,都要pub,否则访问不了(开始在不同目录,后来放到一个文件了,也没调整了,这块后面再补习一下吧)。
xlsx处理lib-umya_spreadsheet
rust处理Excel的lib本来就不是很多,还有很多是只支持读取不支持写入或者只支持写入不支持读取的,好不容易找到了这么一个既支持读又支持写入的,搜了一圈,除了官方文档,几乎没有任何资料。我处理xlsx的时候基本上是按照行进行处理,一行封装成一个model,所以需要知道当前xlsx的当前sheet有多少行数据,然后for循环遍历,组合数据。然而就找一共有多少行这个api就一个一个尝试,官方文档并没有说这块内容,网上也没有这个使用教程,饶了一大圈,最终找到了这个api sheet.get_highest_row()
, 不管怎么说,兜了一大圈,这个总算是解决了。
然后就是下一个问题,写入api需要传一个book对象,因为第二个xlsx实际上是我要追加数据,不是新建一个excel,凭着感觉走,是先用read的api创建mut对象,修改之后吧read的book放到write中,执行回写,验证了一下,hmmm没猜错,这样基本上这个lib的使用问题得到了解决。
for循环
for循环应该是一门语言中最基本的部分了,依靠代码提示,这个没出太大问题,vec和map的循环,都顺利进行,但是拿到一个sheet有多少行之后,我要遍历的时候发现不会了,然后又是搜索引擎走一圈,找到了这个最简单的语法
1 | for i in 1..sheet.get_highest_row()+1 { |
唉,就如实说,光看书是真的不行,还是得coding才能掌握的说。
集合
本来以为集合是个简单的东西,没想到这上面也废了不少劲。简单总结一下:
- Vec 的添加元素用push
- HashSet的添加元素用insert
- HashMap的添加键值对也用insert
- HashMap的遍历调用iter然后for循环,得到的是一个元素,key是.0,value是.1
- 迭代得到的元素是引用,如果要放入其他集合,我选择的是clone一份,直接解引用,经常出现所有权问题,暂时也没想到更好的办法。
字符串 (String和&str)
说真的,字符串这玩意还挺绕的,String和&str,就这两个来来回回的转换,我这也是偷懒了,自己接收也好,使用也罢,基本都用String了,得到了&str基本都转换成String,然后在传递了,最起码没有所有权问题了,偷个懒,后面在慢慢找更好的适配方法。
字符串 (字符串拼接)
我是怎么也没有想到,一个字符串拼接竟然愁住了我。
我从HashSet
1 | let name =element+"流"; |
1 | let name = element.add("流"); |
1 | let name =*element.add("流"); |
我自己尝试出来正确方法
1 | let name = String::from(element).add("流"); |
再来看另外一个, 对于元组的引用,也是通过clone来解决的。。
1 | let mut map: HashMap<String, Vec<Model>> = HashMap::new(); |
全局配置
这个程序因为有个配置文件,运行过程中需要使用,开始想着声明一个全局变量就行了,准备放到dict这个mod里面,存储当然就用HashMap,然后就出现了问题
1 | pub mod dict { |
既然需要HashMap必须是const,我就试一下
1 | const mut map:HashMap<String,String> = HashMap::new(); |
不可变?那我怎么insert配置文件啊?先试试看非mut的
1 | const map:HashMap<String,String> = HashMap::new(); |
给我整不会了,看来HashMap不能全局使用了,从网上又查了一圈,hmm找到方法了,用lazy_static,具体原理还没看,但是尝试了一下,确实解决了我的问题。最终代码如下:
1 | pub mod dict { |
数据类型转换
- f64-> i32 (引申一下,应该数字类型转换都可以这么搞)
let a = num as i32
- String -> i32 (引申一下,字符串到数字类型)
let a = String::from("13").parse::<i32>().unwrap();
总结
这个程序算是第一次正式用rust实现一个完整的功能的程序,说简单吧,也用到不少东西,说复杂吧,实际上就是数据格式转换+xlsx基本操作,后面还会继续用rust实现一些小的功能之类的,主要还是方便一下自己吧,这次代码量也就150+行,不过嘞,写了快一天。
编程这东西,还是不能停留在看,要实际coding才能掌握,后面如果要写一些系统底层的东西,相比现在可要复杂太多了,自己给自己加加油打打气吧~