What to bring on your web journey with Rust
Rust has no built-in HTTP support (just TCP and lower)
Without HTTP, just TCP streams are handled within the Rust std
use std::net::{TcpListener, TcpStream};
fn handle_client(stream: TcpStream) {
// do something
}
fn main() -> std::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:80")?;
for stream in listener.incoming() {
handle_client(stream?);
}
Ok(())
}
The previously example was synchronous. How do we make this asynchronous?
That's a lot to take in...
Thank god, 99% of this is hidden behind a framework if you use one!
Example: axum (using tokio for the runtime and hyper for the HTTP server)
Behind the scenes...
Why is it important to know?
Rust is a low level language - chances are that you might (have to) dig deeper into the stack for a company or project.
Important building blocks: Future, runtime, framework and an HTTP crate
What is a Future?
A Future is a trait.
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll;
}
enum Poll {
Ready(T),
Pending
}
Implementing it ourselves would look like this:
#[derive(Default)]
struct MyFuture {
count: u32,
}
impl Future for MyFuture {
type Output = i32;
fn poll(&mut self, ctx: &Context) -> Poll {
match self.count {
3 => Poll::Ready(3),
_ => {
self.count += 1;
ctx.waker().wake();
Poll::Pending
}
}
}
}
You actively have to "poll" (.await) on a Future to start it/make progress on it.
To do so, the runtime comes into the picture.
Runtime + Kernel
A web framework comes with a HTTP crate (protocol + server implementation).
Popular frameworks are: actix, warp, rocket and axum.
In code (with warp):
use warp::Filter;
#[tokio::main]
async fn main() {
// GET /hello/warp => 200 OK with body "Hello, warp!"
let hello = warp::path!("hello" / String)
.map(|name| format!("Hello, {}!", name));
warp::serve(hello)
.run(([127, 0, 0, 1], 3030))
.await;
}
How does a web service in Rust look like?
cargo doc --open
rustup component add clippy
cargo clippy
warning: length comparison to zero
--> src/routes/question.rs:12:6
|
12 | if params.len() > 0 {
| ^^^^^^^^^^^^^^^^ help: using `!is_empty` is
clearer and more explicit: `!params.is_empty()`
cargo clippy --fix
rustup component add rustfmt
cargo fmt