文章完全参考 Channels 的内容,并没有进行直接翻译,而是使用另一个 redis 库来验证学习文章的例子。在了解 channel 的同时也掌握一些 redis 的编程。首先,我们需要在本机安装一个 redis 实例。
Mac 可以通过 brew 直接安装 redis ,运行如下命令:
brew install redis
不过,时间长了,我们也记不清楚本机上究竟安装了哪些服务,好在可以通过 brew services 来查看本机安装的服务。安装完成后,
启动 redis 服务 :
brew services start redis
停止 redis 服务:
brew services stop redis
被安装的redis的配置文件路径,这个路径比较关键,涉及到修改配置
/usr/local/etc/redis.conf
还可以通过 redis-cli 建立连接,当然,也可以通过更加原始的 telnet 指令建立连接
redis-cli ping
最后一步,从本机卸载 redis
brew uninstall redis
Hello Tokio 的示例代码中,引入了 mini_redis 三方依赖,但这个库本身是不完善的。在它的文档中也明确声明:不要将 mini_redis 引入到生产环境中,这个库只是为 tokio 做教学使用的。
文章决定采用 redis-rs 的库来进行替换,搜索 rust redis 关键字, redis 相关的三方库也挺多的,这个算是比较官方的,这都不重要,channel 才是本文的主角。
关键的一步,给当前示例引入 redis 依赖
[dependencies]
redis = "0.23.3"
下面是使用 redis 的基本例子,首先和本地的 redis 建立连接,然后,调用 set 命令写一个 kv 的数据。最后可以在控制台使用 get 来验证写入是否成功了。
extern crate redis;
use redis::Commands;
fn main() {
do_something();
}
fn do_something() -> redis::RedisResult<()> {
let client = redis::Client::open("redis://127.0.0.1:6379/")?;
let mut conn = client.get_connection()?;
let _: () = conn.set("shop:neo", 35)?;
Ok(())
}
非常有必要来聊一聊 ?的使用情况,如果不熟悉 rust 的话,初次接触问号可能会觉得特别不能理解,我就属于这样,特别想搞清楚。
? 的使用让人很迷惑,硬要去参考Go的话,类似 … 的语法糖,作为一种简化逻辑的手段,也好像是三目运算符。了解问号,不外乎回答清楚 2 个问题:①它的作用是什么?②什么时候使用?
参考《The Rust Programming Language 》附录中的描述,?的表达式如下:
expr?
Error propagation
针对可能出错的操作,可以使用 ? 来做逻辑简化。Go 语言中也有很多可能会出错的系统调用,一般的处理模式都是提供两个返回值,其中一个是 error 类型,当error 不为 nil 时,表示方法执行成功了,然后去使用另一个返回结果。比如,随意选取 go http 包下的一个方法:
func NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*Request, error)
很多时候,考虑要不要处理 error 很头疼。不处理吧,担心它会失败,处理吧,感觉怎么也不应该失败。不过,如果某个函数调用触发了 error ,直接沿着栈的调用链向上抛是最简单的处理方式。当然,也有心累的时候,只要调用的函数中返回了 error,那就无脑处理 error 也没啥问题,每个可能出错的函数都判断一下 error 是否为空,为空的话,就终止当前执行直接向上层抛出 error。
现在已经明确了 ?处理的对象是可能会出错的函数,比如,例子中的 redis 调用 open 函数建立链接,网络总是有不可靠的时候,出错在所难免。
那么,?是如何处理错误的呢?因为单调所有简单,处理方式便是沿着调用栈向上传播错误。如果函数没有返回错误就继续执行,如果有错误,就自动终止当前流程,return 这个错误。
不过,这个自动向上传递的错误类型让我很迷,rust 中的 error 没有类似 go 语言接口 interface 的定义,我向上传递 error 的类型需要符合函数的声明定义。但上面例子中的 ?错误传递链路,是如何保证 error 能顺利地被传递呢?
这引出了 ?另一个关键性问题,它操作的对象类型是什么,这个对象类型需要能明确标志出成功和失败,成功的时候返回结果,失败的时候返回错误。另外,可能也需要有类似 Go interface 的多态性类型,兼容一下类型约束。