気が向いたので Rust でも書いてみるかという感じになって書いてるんですが、TCP Echo サーバーを書くだけで躓いたのでメモ。
fn main() { use std::io::prelude::*; use std::net::{TcpListener, TcpStream}; use std::thread; let listener = TcpListener::bind("127.0.0.1:8124").unwrap(); fn handle_client(mut stream: TcpStream) { std::io::copy(&mut stream.try_clone().unwrap(), &mut stream); } // accept connections and process them, spawning a new thread for each one for stream in listener.incoming() { match stream { Ok(stream) => { thread::spawn(move|| { // connection succeeded handle_client(stream); }); } Err(e) => { /* connection failed */ } } } // close the socket server drop(listener); }
handle_client
内、std::io::copy
の引数に std::io::copy(&mut stream, &mut stream)
とやりたくなるが、ownership が第1引数に渡ってしまうので第2引数が不正とみなされコンパイルエラーになる。それを避けるために try_clone()
しているが、内部的にはファイルディスクリプタの複製をやっているようで、ちょっと無駄っぽい気がする。
std::io::copy
を使わずにこうすれば平気。
fn handle_client(mut stream: TcpStream) { let mut buf = [0; 128]; let mut written = 0; loop { let len = match stream.read(&mut buf) { Ok(0) => break, Ok(len) => len, Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue, Err(e) => break, }; stream.write_all(&buf[..len]); written += len as u64; } }
とはいえ、せっかくのユーティリティ関数使いたいよなぁ。
Rust に詳しい人、ベストプラクティスを教えてください。