Rust中基于Tokio实现P2P节点的类型匹配错误修复求助
解决Tokio Future Join后的类型不匹配问题
咱们先拆解一下你碰到的这个错误:tokio::run对传入的Future有明确要求——它必须是Future<Item = (), Error = ()>类型的,但你的server.join(client)返回的Future的Item是((), ())。这是因为join方法会把两个Future的返回值打包成一个元组,而你的server和client各自的Future都返回(),所以组合后就成了((), ()),和tokio::run期望的单一()类型不匹配。
最简单的修复方式
只需要在join之后加一个map调用,把元组结果转换成()就行:
let future = server.join(client).map(|_| ()); tokio::run(future);
修复后的完整代码
use failure::Error; pub mod networking { use failure::Error; use std::net::SocketAddr; use tokio::net::{TcpListener, TcpStream}; use tokio::prelude::*; use crate::Config; use futures::Future; pub fn start(cfg: &Config) -> Result<(), Error> { let myself = cfg.myself.parse::<SocketAddr>()?; let others = cfg .others .iter() .filter_map(|s| s.parse().ok()) .collect::<Vec<SocketAddr>>(); let server = TcpListener::bind(&myself)? .incoming() .for_each(|socket| { println!("Got a socket: {:?}", socket); future::ok(()) }) .map_err(|e| eprintln!("Error connecting: {:?}", e)); let client = TcpStream::connect(&others[0]) .map(|socket| { println!("Got a socket: {:?}", socket); }) .map_err(|e| eprintln!("Error connecting: {:?}", e)); // 把join后的元组结果转换成(),匹配tokio::run的要求 let future = server.join(client).map(|_| ()); tokio::run(future); Ok(()) } } struct Config { myself: String, others: Vec<String>, } fn main() -> Result<(), Error> { let config = Config { myself: "127.0.0.1:2501".to_string(), others: vec!["127.0.0.1:2502".to_string(), "127.0.0.1:2503".to_string()], }; networking::start(&config) }
额外的优化建议
你的server是一个长期运行的任务(incoming().for_each会持续处理新连接,除非监听出错),而client是一次性的连接操作。用join的话,整个程序会等待两者都完成才会退出——但实际上server会一直运行,所以程序会一直挂着,直到server出错或者你手动终止。
如果更符合你的P2P节点需求,可以用tokio::spawn把client任务放到后台运行,然后直接把server传给tokio::run,这样既解决了类型问题,也更贴合长期运行的场景:
// 把client任务放到后台执行 tokio::spawn(client); // 直接运行server,它会一直监听连接 tokio::run(server);
这样server会持续运行,client连接完成后也不会影响server的正常工作,更适合P2P节点的设计。
内容的提问来源于stack exchange,提问作者Victor Ermolaev
相关产品推荐
相关产品推荐

