昨天我们把minigrep
基本实现了
但是它是相当简陋不优雅的,所以我们来优化下。
这个点我们得先处理,为什么呢?
因为如果等项目大了、掺杂了各种代码之后要调整就有较高的成本了,所以一般我们在开发之前就要想要结构是怎么样的,不过万一开发过程中发现还是需要再调整,这个时候也不晚,尽量避免开发完之后再去调整。
struct
目前我们的项目把代码都堆在main.rs
里,main
文件作为一个入口文件,不应该堆这么多代码的。
我们需要把我们的代码迁移出来放到lib.rs
。
首先需要迁移的理所当然的是我们的struct
。
这里你应该注意到代码里多出了个enum
: ConfigField
,以及一个Config.get
的方法。因为struct
里的字段是私有的,所以这里使用get
方法将它暴露出去。当然,你也可以直接给field
设置为pub
,这样就不用匹配了。
还记得生命周期里的第三板斧吗?
method
是不需要注释生命周期的,所以这里我们不注释也不会报错。
然后我们回到main.rs
中
接下来轮到main
函数了
我们的main
函数
这是一种开发思想,比如我们前端的fetch
[2]就是基于关注点分离的思想架构的。
把一个复杂的问题根据不同的点(侧面)拆分,然后再整合。
目前我们的main
函数中做了四件事
config
实例poem.txt
其中第二点由于我们之前在实现过程中就调整了,所以不需要动。
而第三点第四点实际上是可以合并的,读取poem.txt
这个操作是和匹配行这个行为关联的。我们可以把他们放到同一个函数中。
可以看到我将第三点和第四点合并到一起,我还调用了to_owned
这个method
,因为我们的vector
不能带着引用的数据出run
这个作用域,所以只能把值带走了。
现在main
函数就简洁了。
然后我们还可以把run
方法也放到lib.rs
中,为什么呢?因为这样就可以写测试代码了。
目前我们处理错误都是直接expect(...)
,这样未免有些太过暴力了。毕竟有些我们并不想panic
或者想根据不同错误类型输出不同的错误信息。
我们先来收集下可能会出错的地方
所以存在两个会引发错误的地方,针对这两点我们来改下代码。
第一点其实还能引申出几个小问题点:
panic
,多了不理。所以我们第一点暂时只需要判断参数的个数
那么在哪里加上呢?args.collect
目前是不会报错的,所以我们得考虑其他地方,比如我们创建实例的地方。
这里直接用了panic
,因为是用户输入的,对得起这个规格的待遇(开玩笑的),实际上如果这里不panic
后面也同样会在其他地方panic
,我们也没办法推测或者模拟用户数据去输出一个值。
其实我觉得这样写已经差不多了,毕竟这只是个demo
,怎奈人官方讲究,得用Result
。
官方为啥要这样做呢?
因为官方推荐的是把错误处理抛出来在main
函数中处理,毕竟这样更方便阅读。
我们来改下代码
其中&'static str
之前学可恢复错误的时候也有提到过。
在这里是因为我们的错误提示语是在当前作用域的,引用如果被抛出去了就会变成悬浮引用,那就有问题了。
然后改下main
函数中的调用
是不是优雅了很多~
第二点错误有好几种场景,比如找不到文件或者没有权限等。
我们来改下代码
这里有几个点需要说下:
read_to_string
之前有说过返回一个Result
的类型,所以可以直接调用?
操作符,直接把错误抛出去。当然我们现在还看不懂这个inner
是个啥。
Box
: 这个我们之前有遇到过,就是标明这个函数返回的错误类型现在暂时未知,但是一定是一个实现了std::Error
这个trait
的类型。dyn
也就是dynamic
。ok
然后我们改下main
函数
这样错误也被抛出来了。
实际上我们还可以优化下代码,比如run
函数其实没必要把匹配的结果返回,毕竟我们的项目只是查找并输出,没有后续操作了。
不过这里就不处理了。
TDD
)之前学test
的时候有说到过,binary crate
是没有办法test
的,所以这也就是我们把run
方法迁移到lib.rs
中的原因之一。
另外我们前面完善错误处理也是为了更方便编写测试用例。
那么啥叫测试驱动开发呢(Test-Driven Development
)?
它有如下四个步骤
简单地说就是通过测试用例来提高代码质量。
由于我的步骤和官方的并不一样,所以我现在写完了再去加上测试代码就挺鸡肋的。。。。
我这里就不按照官方给的顺序一步一步把代码实现把测试用例喂肥了,直接一步到位。
lib.rs
中
多亏了这个search
的测试用例,我发现了我的代码中存在错误的地方
最终输出的内容被字母小写了,这是不对的。我们不能去改动原数据。
在实现的过程中,这段代码一直没删除。。。。。
这就成功了~
我们先来写两个测试用例,一个是对大小写不敏感,一个是敏感的
由于我们的代码中对line
做了to_lowercase
,所以只有sensitive
不能通过是正常的。
如果要让sensitive
也通过的话,那就得有个开关控制是否需要to_lowercase
。
那么这个开关放到哪里呢?放到Config
里,因为它也算是用户的输入内容之一。
可以看到我们的代码中多了很多东西,都是对第三个参数的兼容场景。
这回sensitive
也正常了。
可以看到通过命令行传入的方式也是正常的。
但是这么做我们每次都要手动去传入ignore
参数,就很烦,我们来给它个默认值
这样就比较舒服了。
但是这么写未免有些乱,尤其是对参数个数的兼容,很不优雅。
这个时候就轮到环境变量登场了,既能保证不改动太多代码,又能有个默认值的效果。
我们直接来看下代码
是的,这就完事了,在原来的基础上多了一行。
我们来试下
我这里用的powserShell
可以看到返回的是一个Result
的变体Ok
。
is_ok
表示如果有Ok
,那就是true
,也就是说只要你set
了IGNORE_CASE
这个环境变量,那就一定会是true
,只有删除之后才会变回false
。
可以看到使用环境变量比传统方式方便非常多。
rust
中有两种输出(output
): standard output(stdout)
和standard error
。
当前我们所学的内容,错误输出都是通过println!
这个宏,而println!
只能用于标准输出。
那要怎么做才能让错误输出到standard error
呢?
非常简单,输入的终端加上 > output.txt
,就能让错误信息输出到output.txt
文件中。
这种一般交错错误日志,是给开发人员看的。
这一大章我没有跟着官方的节奏来,我是自己先实现一遍之后再优化的。
如果觉得我的代码有问题请自行看官方文档。
编辑于 2022-12-25 14:16・IP 属地广东