Skip to content

Instantly share code, notes, and snippets.

@evanw
Created April 24, 2018 00:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evanw/06a672db1897482eadfbbf37ebf9b9ec to your computer and use it in GitHub Desktop.
Save evanw/06a672db1897482eadfbbf37ebf9b9ec to your computer and use it in GitHub Desktop.
Example crazy Rust compile error
[package]
name = "multiplayer"
version = "0.0.1"
publish = false
[dependencies]
futures = "0.1.18"
hyper = "0.11.17"
tokio-core = "0.1.12"
tokio-signal = "0.1.4"
tokio-tungstenite = "0.5.1"
tungstenite = "0.5.3"
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate tokio_tungstenite;
extern crate tungstenite;
use futures::{Future, Stream};
fn main() {
let address = "127.0.0.1:8080".parse().unwrap();
let mut core = tokio_core::reactor::Core::new().unwrap();
let handle = core.handle();
let server = tokio_core::net::TcpListener::bind(&address, &handle).unwrap();
let socket_server = server.incoming().for_each(move |(stream, address)| {
let handle_clone = handle.clone();
tokio_tungstenite::accept_async(stream).and_then(move |_stream| {
start_session(handle_clone).and_then(move |_session_data| {
println!("Connected to {}", address);
Ok(())
})
// This particular compile error happens when this line is missing
// .map_err(|e| tungstenite::Error::Io(e))
})
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
});
core.run(socket_server).unwrap();
}
fn start_session(handle: tokio_core::reactor::Handle)
-> Box<Future<Item=Vec<u8>, Error=std::io::Error>>
{
let client = hyper::Client::new(&handle);
let url = "https://www.example.com/api/session".parse().unwrap();
Box::new(client.get(url).and_then(|res| {
res.body().concat2().and_then(move |body| Ok(body.to_vec()))
})
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)))
}
$ cargo build
Compiling multiplayer v0.0.1
error[E0271]: type mismatch resolving `<futures::AndThen<std::boxed::Box<futures
::Future<Error=std::io::Error, Item=std::vec::Vec<u8>>>, std::result::Result<(),
std::io::Error>, [closure@src/main.rs:17:44: 20:8 address:_]> as futures::IntoF
uture>::Error == tungstenite::Error`
--> src/main.rs:16:45
|
16 | tokio_tungstenite::accept_async(stream).and_then(move |_stream| {
| ^^^^^^^^ expected struct `std::
io::Error`, found enum `tungstenite::Error`
|
= note: expected type `std::io::Error`
found type `tungstenite::Error`
error[E0599]: no method named `map_err` found for type `futures::AndThen<tokio_t
ungstenite::AcceptAsync<tokio_core::net::TcpStream, tungstenite::handshake::serv
er::NoCallback>, futures::AndThen<std::boxed::Box<futures::Future<Error=std::io:
:Error, Item=std::vec::Vec<u8>>>, std::result::Result<(), std::io::Error>, [clos
ure@src/main.rs:17:44: 20:8 address:_]>, [closure@src/main.rs:16:54: 22:6 handle
_clone:_, address:_]>` in the current scope
--> src/main.rs:23:6
|
23 | .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
| ^^^^^^^
|
= note: the method `map_err` exists but the following trait bounds were not s
atisfied:
`futures::AndThen<tokio_tungstenite::AcceptAsync<tokio_core::net::Tcp
Stream, tungstenite::handshake::server::NoCallback>, futures::AndThen<std::boxed
::Box<futures::Future<Error=std::io::Error, Item=std::vec::Vec<u8>>>, std::resul
t::Result<(), std::io::Error>, [closure@src/main.rs:17:44: 20:8 address:_]>, [cl
osure@src/main.rs:16:54: 22:6 handle_clone:_, address:_]> : futures::Future`
`&mut futures::AndThen<tokio_tungstenite::AcceptAsync<tokio_core::net
::TcpStream, tungstenite::handshake::server::NoCallback>, futures::AndThen<std::
boxed::Box<futures::Future<Error=std::io::Error, Item=std::vec::Vec<u8>>>, std::
result::Result<(), std::io::Error>, [closure@src/main.rs:17:44: 20:8 address:_]>
, [closure@src/main.rs:16:54: 22:6 handle_clone:_, address:_]> : futures::Stream
`
`&mut futures::AndThen<tokio_tungstenite::AcceptAsync<tokio_core::net
::TcpStream, tungstenite::handshake::server::NoCallback>, futures::AndThen<std::
boxed::Box<futures::Future<Error=std::io::Error, Item=std::vec::Vec<u8>>>, std::
result::Result<(), std::io::Error>, [closure@src/main.rs:17:44: 20:8 address:_]>
, [closure@src/main.rs:16:54: 22:6 handle_clone:_, address:_]> : futures::Future
`
error: aborting due to 2 previous errors
error: Could not compile `multiplayer`.
To learn more, run the command again with --verbose.
@evanw
Copy link
Author

evanw commented Jun 23, 2020

Go wasn't ruled out exactly in that I didn't do a formal comparison between the two before building it. I was experimenting with Rust at the time and had a working Rust implementation, then the servers started falling over from load and we needed to use it.

That said, the multiplayer server does have some unique requirements that Rust is a good fit for. The multiplayer service is extremely memory-intensive and garbage-collected languages inherently add additional overhead because of uncollected garbage. I also wanted to have an individual process per document for robustness and Rust's smaller binary size and lack of a runtime meant this strategy was actually practical. The multiplayer server is basically a real-time graph database and generics were nice to have for working with graph data structures. The code for the multiplayer server is also pretty small so Rust's bad compile times are tolerable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment