I am new to Rust. I went through tutorials/play with several small projects and feel comfortable with straightforward code.
However, there is one thing where I constantly get stuck. It's more of a question of how to understand Rust (vs solving specific programming problem).
The place where I get stuck is understanding what should I pass and return from some (complex) functions exposed by some libraries.
Just to give you an example hyper.rs has two functions:
- make_service_fn
- service_fn
Hello world (using these function may look something like this):
use std::{convert::Infallible, net::SocketAddr};
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};
async fn handle(_: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new("Hello, World! {}".into()))
}
#[tokio::main]
async fn main() {
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let make_svc = make_service_fn(|_conn| async {
Ok::<_, Infallible>(service_fn(handle))
});
let server = Server::bind(&addr).serve(make_svc);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
}
And here, two things that puzzle me:
a) That we wrap everything in Ok()
in handle function
b) That we wrap Ok::<_, Infalliable>
inside make_service_fn
Obviously, it’s needed somewhere in .serve()
. However, I can’t seem to find a good way to understand what is exactly expected by serve? What make_service_fn should take as an argument and return? What service_fn should take as an argument and return?
I can look at the documentation of each function. However, they don’t talk about a need to have Result (vs different struct or enum).
And if I try to change the code and remove Result<> and Ok wrapping, it complains about quite strange (for me) errors in a couple of places.
This is not the only place where I got stuck with something like that. It feels like some libraries/frameworks have a complicated mix of traits, generics, trait bounds, sprinkled with Futures. The beauty and the curse that the language syntax hides all of this (that Rust compiler figures out under the hood which types each thing are).
The question which I have. How do you untangle these things? I tried to start from each end and completely got lost in a level of indirections and things wrapped one into each other. It feels like I am trying to solve it using a method that works well on other languages but doesn't work well on Rust libraries.
Any thoughts/recommendations?