2

(一)在这里,(二)在这里,下面是(三)。

神奇的 I/O 异常

折腾了半天,终于要开始写 I/O 了。首当其冲的是一个 TCP 服务端 socket:

impl TcpListener {
    pub fn set_address(&self, address: &str) {
        let addr = from_str::<SocketAddr>(address).unwrap();
        let listener = tcp::TcpListener::bind(addr);
        let mut acceptor = listener.listen();
        for stream in acceptor.incoming() {
            spawn(proc() {
            });
        }
        drop(acceptor);
    }
}

其实这段程序没什么神奇的啦,神奇的事情出现在 use 上:

use std::io::net::tcp;
use std::io::net::ip::{SocketAddr, Ipv4Addr};

起初我只是这样写了这么两句,没想到官方文档抄来的代码居然编译出错了:

tcp_listener.rs:16:28: 16:45 error: type `std::result::Result<std::io::net::tcp::TcpListener,std::io::IoError>` does not implement any method in scope named `listen`
tcp_listener.rs:16         let mut acceptor = listener.listen();
                                              ^~~~~~~~~~~~~~~~~
error: aborting due to previous error

参考出错信息,查阅了一下源码,人家 bind() 返回的确实是一个 Result,这么生猛地直接调用 listen() 确实说不过去。

但是为什么文档里的例子这么写呢?

原来我漏掉了一句 use

use std::io::{Acceptor, Listener};

虽然是没有地方直接用到他们,但是自从加上这两个之后,编译就神奇的通过啦!

还是得看文档。人家说了,这是因为 I/O 操作经常会出错,为了避免通篇的处理 IoResult,特意制作的这种方便用法——可以不手动解包 IoResult 而直接当作成功结果来使用,只是需要 use std::io 下面的一些东西。

这是怎么做到的?!

原来这里的猫腻都在 libstd/io/result.rs 里面。比如刚才碰到的 Listener 这个:

impl<T, A: Acceptor<T>, L: Listener<T, A>> Listener<T, A> for IoResult<L> {
    fn listen(self) -> IoResult<A> {
        match self {
            Ok(listener) => listener.listen(),
            Err(e) => Err(e),
        }
    }
}

哈!Rust 还能这么搞!生生给 IoResult<Listener...> 包了一个函数进去,怪不得可以直接调用 listen 了呢。于是,我默默地想,zmq.rs 要不要也照猫画虎这么搞,貌似很省事儿哦。


fantix
1.7k 声望174 粉丝

Linux、Python 与开源爱好者一枚,GINO 项目作者,EdgeDB 团队成员。