2018-06-10
The last months I have seen a lot of blog posts writing about the Rust programming language โ sometimes how good it is, sometimes why it is better than other languages. Then I came across Writing an OS in Rust and thought: why not give Rust a try?
Inspired by this I am trying Rust for a few hours: understanding principles and design goals, and building something simple. I will read some documentation, set up a local dev environment, develop a hello world program and then something a bit more complex.
I started by reading:
The Arch Linux rustup package from pacman didn't work for me โ I missed configuring a default toolchain. So I went with the official install:
curl https://sh.rustup.rs -sSf | sh
source $HOME/.cargo/env
To update later:
rustup update && cargo update
Verify versions:
$ rustc --version && cargo --version
rustc 1.26.2 (594fb253c 2018-06-01)
cargo 1.26.0 (0e7c5a931 2018-04-06)
The full installation was only 13 MB:
$ du -sh ~/.cargo
13M /home/benni/.cargo
Create hello.rs:
/// The main function
fn main() {
// Print text
println!("Hello World!");
}
Compile and run:
rustc hello.rs
./hello
After hello world I wanted something more serious: a simple REST API with unit tests.
Create the project:
cargo new hello-api --bin && cd hello-api
Add dependencies to Cargo.toml:
[dependencies]
rocket = "0.3.12"
rocket_codegen = "0.3.12"
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
[dependencies.rocket_contrib]
default-features = false
features = ["json"]
Add src/main.rs:
#![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)]
extern crate rocket;
#[macro_use] extern crate rocket_contrib;
#[macro_use] extern crate serde_derive;
#[cfg(test)] mod tests;
use rocket_contrib::{Json, Value};
#[derive(Serialize, Deserialize)]
struct HelloInput {
name: String
}
#[get("/", format = "application/json")]
fn index() -> Json<Value> {
Json(json!({ "status": "OK" }))
}
#[post("/", format = "application/json", data = "<json_input>")]
fn hello(json_input: Json<HelloInput>) -> Json<Value> {
Json(json!({ "hello": json_input.name }))
}
#[error(404)]
fn not_found() -> Json<Value> {
Json(json!({ "status": "error", "reason": "Resource was not found." }))
}
fn rocket() -> rocket::Rocket {
rocket::ignite()
.mount("/", routes![index])
.mount("/hello", routes![hello])
.catch(errors![not_found])
}
fn main() {
rocket().launch();
}
The initial build failed because Rocket 0.3 required nightly:
rustup default nightly
rustup update && cargo update
cargo build
Run it:
$ cargo run
๐ Rocket has launched from http://localhost:8000
Test with curl:
$ curl http://localhost:8000/
{"status":"OK"}
$ curl --request POST \
--header "Content-Type: application/json" \
--data '{"name":"benni"}' \
http://localhost:8000/hello
{"hello":"benni"}
Add src/tests.rs:
use rocket;
use rocket::local::Client;
use rocket::http::{Status, ContentType};
#[test]
fn test_not_found() {
let client = Client::new(rocket()).unwrap();
let res = client.get("/not_found").header(ContentType::JSON).dispatch();
assert_eq!(res.status(), Status::NotFound);
}
#[test]
fn test_alive() {
let client = Client::new(rocket()).unwrap();
let mut res = client.get("/").header(ContentType::JSON).dispatch();
let body = res.body_string().unwrap();
assert_eq!(res.status(), Status::Ok);
assert!(body.contains("OK"));
}
#[test]
fn test_post_hello() {
let client = Client::new(rocket()).unwrap();
let mut res = client.post("/hello")
.header(ContentType::JSON)
.body(r#"{ "name": "benni" }"#)
.dispatch();
assert_eq!(res.status(), Status::Ok);
let body = res.body().unwrap().into_string().unwrap();
assert!(body.contains("hello"));
}
$ cargo test
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
My first try with Rust took about 6 hours. After that I could write simple programs with tests and work with the CLI and dependencies. Compared to Go, which I learned 1.5 years earlier, the learning curve felt similar. What I appreciated most:
The syntax felt a bit strange at first, especially macros and extern. I expect it gets easier with more use. I'm planning to invest more time in Rust to see if it fits my personal purposes.
Note: this article uses Rocket 0.3 (2018). The Rocket API has changed significantly since then.
References