9.9 KiB
class: center, middle
Full stack Rust
Hyper Diesel Rust Rocket
class: center, middle

Wenn es fertig ist sieht das ganze so aus...
???
- Ein bisschen rostig für den Charme
- Flink
- Mit kleinem sidekick
- Mit ein bisschen glück kann man auch den Todesstern vernichten.
class: center, middle
Wenn man genau hinschaut ist sogar R2D2 mit an Board:

???
Und tausend anderen Modulen, hello javascript
class: center, middle
In Go sähe es dann so aus:

???
Ok ein bisschen Spaß muss sein...
Übersicht
- Wer bin ich
- Rust im Backend
- Rust für Datenbanken
- Rust im Browser (WASM)
Wer bin ich
- Backend-Entwickler bei anfema
- Normalerweise schreibe ich Python und Node.js
- Code seit 2003 in diversen Bereichen
- Enttäuscht von Swift
--
- Manche sagen ich bin ein irrer Bastler
--
- Wenn ich nicht Code bastel ich Elektronikgerümpel
Rust im Backend
- Es gibt verschiedene Frameworks
--
- Iron
- Nickel
- Conduit
- Rocket
- Gotham
- ...
???
- Iron: Juni 2014, Aktuell kein Maintainer
- Nickel: Juni 2014, API ähnlich Express
- Conduit: Juni 2014, Wahrscheinlich tot
- Rocket: März 2016, Aktive Entwicklung
- Gotham: März 2017, Viele Beispiele, Aktive Entwicklung
--
-
Das Benutzerfreundlichste ist Rocket (IMHO)
Beispiel: Rocket
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
#[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();
}
???
- Wir brauchen Compiler Plugins
- ... und den Rocket Code-Generator für weniger Boilerplate
- Dann bauen wir uns einen Endpunkt
- Python Flask
- Return String,
format
ist ein macro - Start mit einer einzelnen route
JSON API Beispiel
Model
#![feature(custom_derive)]
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate chrono;
use chrono::prelude::*;
#[derive(Serialize, Deserialize, Debug)]
pub struct Person {
pub id: i32,
pub name: String,
pub birthday: NaiveDateTime,
}
???
- Wieder ein neues rust-Feature
- Serde: Serializer-Deserializer
- Chrono: Date/Time lib
- Boilerplate lassen wir wieder generieren
Person
ist unser Model
View
use rocket::response::Failure;
use rocket_contrib::Json;
#[get("/person/<id>", format = "application/json")]
fn get_person(id: i32) -> Result<Json<Person>, Failure> {
Ok(Json(Person {
id,
name: String::from("Max Musterman"),
birthday: Local::now().naive_local(),
}))
}
Die anderen HTTP Methoden funktionieren natürlich auch...
???
- JSON-Support laden
- Mock-Endpoint
- Statische Daten als Beispiel
- Reagiert nur auf
Accept: application/json
Ergebnis
???
Accept
-Header nicht vergessen sonst 404- JSON formatiert zur besseren lesbarkeit
Request Guards (aka Middleware)
#[get("/admin")]
fn admin_panel(admin: AdminUser) -> &'static str {
"Hello, administrator. This is the admin panel!"
}
#[get("/admin", rank = 2)]
fn admin_panel_user(user: User) -> &'static str {
"You must be an administrator to access this page."
}
#[get("/admin", rank = 3)]
fn admin_panel_redirect() -> Redirect {
Redirect::to("/login")
}
Rocket wählt automatisch denjenigen View der alle Request guards unterstützt (und den niedrigsten Rank hat)
???
- Middleware als opt-in
- Die Parameter-Typen implementieren
FromRequest
-Trait - Nach Rank
- Request Guards müssen erfüllt werden sonst 404
format
im Decorator funktioniert ähnlich
Rust für Datenbanken
- Auch hier wieder verschiedene Dinge:
-- - Postgres-Lib direkt - Rustorm - Diesel - ... ???
- Postgres-Lib: Nicht direkt typesafe, bzw dynamisch
- Langsam
- Rustorm: Rewrite in progress, schlechte Doku
- Diesel: Dürftige Doku aber es geht voran
-
Schneller als postgres-lib direkt
-
- Ich habe mich auf Diesel eingeschossen weil es typesafe ist.
Model definition (Diesel)
- Table
table! {
person (id) {
id -> Integer,
name -> Text,
birthday -> Timestamp,
}
}
- Model
#[derive(Serialize, Deserialize, Debug, Queryable, \
Insertable, Identifiable, AsChangeset)]
#[table_name = "person"]
pub struct Person {
...
???
- Migrations: SQL direkt
table!
definition lässt sich mitdiesel-cli
automatisieren- Model wie vorher, mehr
derive
- Boilerplate wird wieder generiert
View (Diesel)
#[get("/persons")]
pub fn get_person_list(conn: DbConn)
-> QueryResult<Json<Vec<Person>>>
{
person::table
.order(person::id.asc())
.load::<Person>(&*conn)
.map(|person| Json(person))
}
#[get("/person/<id>")]
pub fn get_person(id: i32, conn: DbConn)
-> Result<Json<Person>, Failure>
{
person::table
.find(id)
.first::<Person>(&*conn)
.map_err(|_| Failure(Status::NotFound))
.map(|person| Json(person))
}
???
- Sehr vereinfacht
- Kein DB-Connection Error-Handling
DbConn
kommt als Request-Guard- Kaputte DB -> 404
map
konvertiert nur Successmap_err
konvertiert nur Error
View (continued)
#[post("/person", data="<data>")]
pub fn create_person(data: Json<Person>, conn: DbConn)
-> Result<Json<Person>, Failure>
{
let rows_inserted = insert_into(person::table)
.values(&data.into_inner())
.execute(&*conn)
.unwrap();
if rows_inserted != 1 {
Err(Failure(Status::InternalServerError))
} else {
person::table
.order(person::id.desc())
.first::<Person>(&*conn)
.unwrap()
.map(|person| Json(person))
}
}
???
- Kurzes beispiel für nen DB-Save
- Der
DbConn
-Request-Guard erzeugt und committed transaction - Nur deshalb ist der Query-Code safe
- Insert and Return geht noch nicht
- Serde und Diesel übernehmen das Decoding der
Person
- Kein error handling beim insert:
unwrap()
crashed den worker -> 500
Rust im Browser
- wasm-bindgen und Webpack, JS module in Rust
- yew, React-style Framework, Everything Rust
???
- Beides nur kurz angerissen
- Alles sehr alpha
- Aber es funktioniert und wird weiterentwickelt
WebAssembly Module für Javascript
Rust Teil
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}
???
-
Wir brauchen
bindgen
und ne Menge nightly Kram -
Bindgen baut den Typ-Konverter von JS -> WASM
-
Die Bindgen Macros bauen Typ-Konverter WASM -> Rust
-
Externe Funktion
alert
, kann aber jede beliebige JS funktion sein -
Export von unserer
greet
funktion für JS
Vorbereiten
$ rustup target add wasm32-unknown-unknown --toolchain nightly
$ cargo install wasm-bindgen-cli
Compile
$ cargo +nightly build --target wasm32-unknown-unknown
$ wasm-bindgen target/wasm32-unknown-unknown/debug/wasm_greet.wasm \
--out-dir .
Javascript
const rust = import("./wasm_greet");
rust.then(m => m.greet("World!"));
???
-
Erstmal brauchen wir tooling
-
Dann compilen wir den Rust-Code
-
Dann bauen wir den JS Glue-Code
-
In Production sollte das dann Webpack machen
-
Am Ende ist es einfach ein JS module
Direkt in Rust rendern: Yew
https://github.com/DenisKolodin/yew
- JSX Style templates mittels html! macro direkt im Rust code
- Application state management mittels Message passing
- ReactJS und elm waren die inspiration
- Eigener Virtual DOM
- Components und Fragments wie bereits bekannt
???
- Nur angerissen was theoretisch geht
- Projekt existiert seit Dezember 2017
- TODO-MVC geht aber schon
--
- Very Alpha
???
- Das ist als Warnung zu verstehen, Ich kenne ja die Javascript leute...
Dank crates.io findet man aber noch viele Zusatzmodule
--
???
So wird dann aus unserem kleinen X-Wing ein großer Millenium Falcon.
Aber trotzdem:
This is the ship that made the Kessel Run in fourteen parsecs?
Es gibt übrigens noch kein Crate namens Solo.
C3PO ist leider an den verkehrten Anwendungsfall gebunden, es wäre so schön gewesen wenn das ein I18N Crate wäre.
class: center, middle
Danke für's Zuhören
Kommt gerne auf mich zu wenn ihr mehr wissen wollt!
Are we web yet? http://www.arewewebyet.org/
Quellen
Kontakt
- hallo@dunkelstern.de
- Twitter: @dunkelstern
- Blog: blog.dunkelstern.de