23 KiB
Rust, Rocket and Diesel
- Rust
- Rocket
- Diesel
- Lets code something
- Conclusion
Rust
What is Rust?
#+LaTeX:∈cludegraphics[width = 0.65\textwidth]{img/Bruine_roest_op_tarwe_(Puccinia_recondita_f.sp._tritici_on_Triticum_aestivum).jpg}
Rust, the language
- About 10 years young (2010)
- Memory safe without gcing, optional ref counting
- Ownership, lifetimes, traits, functional paradigms
- Variables are immutable by default and can be shadowed
- Performance of idiomatic Rust is comparable to the performance of idiomatic cpp
How to install
Install Rust
$ curl --proto '=https' --tlsv1.2 -sSf \
https://sh.rustup.rs | sh
Install nightly Rust (needed for Rocket)
$ rustup toolchain install nightly
Install rustfmt (linter and code formatter)
$ rustup component add rustfmt
Install clippy (static code analysis)
$ rustup component add clippy
Where to find more information?
Docs…
- The Rust Book (
$ rustup docs --book
) - Rust by Example (
$ rustup docs --rust-by-example
) - The Rust Standard Library (
$ rustup docs --std
) - Use '
$ rustup help docs
' to get an overview - cheats.rs
- programming-idioms.org
- crates.io/lib.rs
Hello, World!
Executing `cargo new foobar`
creates a new project…
foobar/Cargo.toml
[package]
name = "foo"
version = "0.1.0"
authors = ["finga <finga@onders.org>"]
edition = "2018"
\tiny
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
\normalsize
[dependencies]
foobar/src/main.rs
fn main() {
println!("Hello, world!");
}
Mutability and shadowing
Immutable variable
let foo = 5;
Mutable variable
let mut bar = 5;
bar = 6;
Shadowing
let baz = 5;
let baz = baz + 3;
Structs
Definition of a struct
struct Something {
id: usize,
item: String,
}
Instantiation of a struct
let something = Something {
id: 0,
item: String::from("an item"),
};
Access of a struct's field
something.item
Tuple structs
Definition of a tuple struct
struct Box(usize, usize, usize);
Instantiation of a tuple struct
let first_box = Box(3, 5, 7);
Access of a tuple struct's field
first_box.0;
first_box.1;
first_box.2;
Error related types
- Heavy use of
Option<T>
andResult<T, E>
Option
pub enum Option<T> {
None,
Some(T),
}
Result
pub enum Result<T, E> {
Ok(T),
Err(E),
}
Error handling
Unrecoverable errors → panics
fn get_data() -> Data {
...
panic!("Cannot get data");
}
Recoverable error with anyhow
fn get_data() -> Result<Data> {
...
Ok(data) // return is only used for "early" returns
}
...
let data = get_data()?;
...
match
statements
- Must be exhaustive
- All match arms must return the same type!
- Can be used everywhere e.g.
let foo = match ...
- All match arms must return the same type!!1!
Recoverable error with anyhow
match age {
0 => unreachable!("Too young to be a person"),
n @ 1..=12 => println!("Child of age {}", n),
n @ 13..=19 => println!("Teen of age {}", n),
n @ 20..=150 => println!("Person of age {}", n),
_ => unimplemented!("Nobody I know got that old"),
}
if let
statements
Sometimes an if let
is nicer than a match
or an if/else
statement
if let Some(actual_age) = age {
println!("The age is {}", actual_age);
}
Use functional language features
vec![1, 2, 3].iter()
vec![1, 2, 3].iter().sum()
vec![1, 2, 3].iter().map(|x| x + 1).collect()
foo.into_iter().filter(|i| i.bar == 0).collect()
Iterator
trait
Tests
- Use
assert!()
family -
Unit tests
- Put them into a test module
- Annotate the test module with
#[cfg(test)]
- Annotate the tests with
#[test]
- Documentation testing
-
Integration testing
- Integration tests are put into the
src/tests/
directory
- Integration tests are put into the
Docs
- Documentation comments:
///
- Markdown notation
cargo doc
runnsrustdoc
which generates html docscargo doc --open
runsrustdoc
and opens the result in a browserrustdoc
documentation: https://doc.rust-lang.org/beta/rustdoc/index.html
Rocket
What is Rocket?
Rocket is a web framework for Rust that makes it simple to write fast, secure web applications without sacrificing flexibility, usability, or type safety.
Features
Col left
- Type Safe
- Extensible
- Templating
- (Secure) Cookies
Col right
- Streams
- Testing (Unit tests)
- Typed URIs
- Single binary
Create a new Rocket project
Create the crate
cargo init new_project
cd new_project
rustup override set nightly
cargo r
Add Rocket dependencies to Cargo.toml
[dependencies]
rocket = "0.4"
Hello Rocket
src/main.rs
#![feature(decl_macro)]
use rocket::{get, routes};
#[get("/hello/<name>/<age>")]
fn hello(name: String, age: u8) -> String {
format!("Hello, {} year old named {}!", age, name)
}
fn main() {
rocket::ignite().mount("/", routes![hello]).launch();
}
Receive data
Struct EmailForm
#[derive(Debug, FromForm)]
struct EmailForm {
email: String,
}
Procedure using that form data
#[post("/reset", data = "<email>")]
fn reset_email(email: Form<EmailForm>) -> Flash<Redirect> {
...
Flash::success(
Redirect::to(uri!(reset)),
format!("Email was sent to {}", email.email),
)
}
Flash messages
#[get("/reset")]
fn reset(flash: Option<FlashMessage>) -> String {
if let Some(ref msg) = flash { // `let ref x = y` is
msg.msg() // equivalent to `let x = &y`
} else {
format!("Hello, reset!");
}
}
Templates
- Use the
rocket_contrib
crate!
templates/name.html.tera
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<p> Hello,
{{ name }}!</p>
</body>
</html>
src/main.rs
#[get("/hello/<name>")]
fn hello(name: String) -> Template {
let mut context = HashMap::new();
context.insert("name", name);
Template::render("name", &context)
}
...
rocket::ignite()
.mount(...)
.attach(Template::fairing())
.launch();
Where to get further information?
Chat
- Matrix: #rocket:mozilla.org
- IRC on Freenode: #rocket
Diesel
ORM
Object-relational-mapping is a programming technique for converting data between incompatible type systems using the paradigms of your programming language.
Pros
- Models are DRY (don't repeat yourself)
- Cleaner separation
- Prepared and sanitised queries
- Changes are versioned
Cons
- Debugging can get more complicated (performance)
- No need to learn SQL
Prerequisites diesel_cli
Setup Diesel for default DBMS
$ sudo apt install libpq-dev libsqlite3-dev \
default-libmysqlclient-dev
$ cargo install diesel_cli
Setup Diesel for SQLite only
$ sudo apt install libsqlite3-dev
$ cargo install diesel_cli -no-default-features \
--features sqlite
How does that look in Rust
Define a struct
#[derive(Queryable)]
pub struct NewComment<'a> {
name: &'a str,
email: &'a str,
comment: &'a str,
}
Insert something
let user = NewUser {
name: "foo",
email: "foo@bar.baz",
comment: "A comment",
};
...
insert_into(comments)
.values(&user)
.execute(conn);
About Diesel
- Enable connection pooling via the
r2d2
crate - Nested joins are not yet nicely implemented
- Database specific SQL regarding each DBMS
- Take a look at the diesel_cli crate for multi DBMS support
- Use
diesel_cli
macros and functions for compiling migration functionality into the application
Where to get further information?
Docs…
- diesel.rs
- Diesel Guides
- Diesel API docs
- Examples (minimalistic)
Support
Lets code something
Lets create a small web app
A chat box where everybody can post.
#+LaTeX:∈cludegraphics[width = 0.7\textwidth]{img/challenge-accepted-lets-code-it.jpg}
Conclusion
In general
- Rust's community and ecosystem is growing steadily
- Rust covers a broad field from low level code to high level
- Rocket and libraries alike improve creating security and performance focused web application development
- Diesel is on a good way to become a serious ORM
- Alltogether Rust is ready to be used for web apps
What did we do?
- Setup Rust and Diesel
- Create a project with Rocket and Diesel prerequisites
- Store a small model with Diesel in a SQLite DB
- Create and use Tera templates
Where to continue
- Move DB setup into the project
- Add features (List posts by Author, paging, etc..)
- (Private) Cookies
Two small projects
The End
#+LaTeX:∈cludegraphics[width = 0.3\textwidth]{img/goodbye.jpg}
\Huge Thanks for your attention!!1!
This is the end..