Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rust as a CLI language

Rust as a CLI language

Talk given at Linuxing in London

Florian Gilcher

January 17, 2018
Tweet

More Decks by Florian Gilcher

Other Decks in Programming

Transcript

  1. • Rust and Elasticsearch Trainer • Event organiser • Ruby

    Programmer since 2003 • Rust Programmer since 2013 • CEO asquera GmbH
  2. • new systems programming language • powers and was developed

    in along with Servo, a new browser engine • by Mozilla and the Community • First stable release May 15th, 2015
  3. • Static type system with local type inference • Explicit

    mutability • Zero-cost abstractions • Runtime-independent concurrency safety
  4. • Errors are values • No null • Static automatic

    memory manage- ment • No garbage collection
  5. extern crate tempdir; use tempdir::*; use std::fs::File; fn main() {

    let tempdir = TempDir::new("goto-berlin"); let mut tempfile = match tempdir { Ok(dir) => { File::create( dir.path().join("tmpfile") ) } Err(_) => { panic!("Couldn’t open tempdir") } } do_something(&mut tempfile); // look, no close necessary! }
  6. struct InnerData { val: i32 } struct Data { inner:

    InnerData } fn main() { let d = Data { inner: InnerData { val: 41 }}; d.inner.val = 42; // error: cannot assign to immutable field `d.inner.val` }
  7. struct InnerData { val: i32 } struct Data { inner:

    InnerData } fn main() { let mut d = Data { inner: InnerData { val: 41 }}; d.inner.val = 42; }
  8. • Every piece of data is uniquely owned • Ownership

    can be passed • When owned data reaches the end of a scope, it is destructed
  9. use std::fs::File; use std::io::Write; fn main() { let file =

    File::open("test") .expect("Unable to open file, bailing!"); take_and_write_to_file(file); // take_and_write_to_file(file); // ^^ Illegal } fn take_and_write_to_file(mut file: File) { writeln!(file, "{}", "Hello #gotober!"); }
  10. • Access can be borrowed (mutable and immutable) • You

    can borrow mutably once • Or multiple times immutably • Exclusive: mutable or immutable, never both
  11. use std::fs::File; use std::io::Write; fn main() { let mut file

    = File::open("test") .expect("Unable to open file, bailing!"); write_to_file(&mut file); write_to_file(&mut file); } fn write_to_file(file: &mut File) { writeln!(file, "{}", "Hello #gotober!"); }
  12. error[E0502]: cannot borrow `vector` as mutable –> src/main.rs:4:5 | 3

    | let elem = &vector[1]; | —— immutable borrow occurs h 4 | vector[2] = 4; | ^^^^^^ mutable borrow occurs here 5 | } | - immutable borrow ends here
  13. struct Data<’a> { inner: &’a i32 } fn return_reference<’a>() ->

    Data<’a> { let number = 4; Data { inner: &number } }
  14. –> src/main.rs:8:20 | 8 | Data { inner: &number }

    | ^^^^^^ does not live long 9 | } | - borrowed value only lives until here |
  15. All Rust function signatures not only signal data types, but

    also mutability, ownership and interconnections between input and output types.
  16. • Borrows boil down to pointers at runtime • Values

    are plain values just like in e.g. C • Optional unsafe sub-language
  17. rustup In general, it is recommendable to target a rustc

    that is shipped with your target system.
  18. We’re currently collecting community blogposts and I lost count. Luckily,

    a kind soul offers a JSON feed on readrust.net.
  19. { "version": "https://jsonfeed.org/version/1", "title": "#Rust2018", "home_page_url": "http://readrust.net/", "feed_url": "http://readrust.net/rust2018/feed "description":

    "Posts from the Rust 2018 initi "author": { "name": "Wesley Moore", "url": "http://www.wezm.net/" }, "items": [ {
  20. Quick error handling in CLI let file = File::open("foo").unwrap(); //

    This will quit if the File cannot be opened
  21. Strings Rust has 2 types of strings: &str and String.

    The first is harder to use then the second. They are compatible.
  22. Quickly handling Strings At the beginning, whenever you encounter &str

    and are confused, use this: let heap_allocated_string = "normal_string".to_string();
  23. Task A proper CLI tool that gets the feed, parses

    it, prints it in a nice, readable fashion.
  24. readrust 0.1 Florian G. <[email protected]> Reads readrust.net USAGE: readrust [FLAGS]

    [OPTIONS] FLAGS: -c, –count Show the count of posts -h, –help Prints help information -V, –version Prints version information OPTIONS: -n, –number <NUMBER> Only print the NUMBE
  25. cargo manifest [package] name = "readrust" version = "0.1.0" authors

    = ["Florian Gilcher <florian.gilcher@asq [dependencies] reqwest = "0.8" clap = "2.29" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" prettytable-rs = "0.6"
  26. extern crate clap; use clap::App; fn main() { let app

    = App::new("readrust") .version("0.1") .author("Florian G. <[email protected]>") .about("Reads readrust.net") .args_from_usage("-n, –number=[NUMBER] ’Only print the NUMBER most recent posts’ -c, –count ’Show the count of posts’"); let matches = app.get_matches(); }
  27. $ cargo run – –help readrust 0.1 Florian G. <[email protected]>

    Reads readrust.net USAGE: readrust [FLAGS] [OPTIONS] FLAGS: -c, –count Show the count of posts -h, –help Prints help information -V, –version Prints version information OPTIONS: -n, –number <NUMBER> Only print the NUMBER most recent posts
  28. extern crate reqwest; pub static URL: &’static str = "http://readrust.net/rust2018/fee

    fn get_feed() -> String { let client = reqwest::Client::new(); let request = client.get(URL); let mut resp = request.send().unwrap(); assert!(resp.status().is_success()); resp.text().unwrap() }
  29. • Generic framework • Generic and strict parse modes •

    JSON parser one of the fastest around • good documentation
  30. #[derive(Debug, Deserialize, Serialize)] pub struct Item { id: String, title:

    String, content_text: String, url: String, date_published: String, author: Author, }
  31. #[derive(Debug, Deserialize, Serialize)] struct Feed { version: String, title: String,

    home_page_url: String, feed_url: String, description: String, author: Author, items: Vec<Item>, }
  32. fn get_feed() -> Feed { let client = reqwest::Client::new(); let

    mut request = client.get(URL); let mut resp = request.send().unwrap(); assert!(resp.status().is_success()); let data = resp.text().unwrap(); serde_json::from_str::<Feed>(&data).unwrap() }
  33. fn fn print_feed_table<I: Iterator<Item = Item>>(items: I) { let mut

    table = prettytable::Table::new(); table.add_row(row!["Title", "Author", "Link"]); for item in items { let title = if item.title.len() >= 50 { &item.title[0..50] } else { &item.title }; table.add_row(row![title, item.author.name, item.url]); } table.printstd(); }
  34. Using it if matches.is_present("count") { print_count(&feed); } else { let

    iter = feed.items.into_iter(); if let Some(string) = matches.value_of("number") { let number = string.parse().unwrap(); print_feed_table(iter.take(number)) } else { print_feed_table(iter) } }
  35. • A 79-line program • With full build and dependency

    tooling • No typing ceremony except the Feed definition
  36. • Easily shippable as a binary • Full error handling

    (even if we just bail!) • Fully documented dependencies
  37. When and why to pick up Rust • You want

    to learn something new • You want that extra bit of perfor- mance • You want the control of C, but the safety of Python • You want to parallelise things
  38. Continuing • Do proper error passing and central handling •

    Add logging (using... log!) • Filter the items • Make statistics over the items
  39. Rust is a surprisingly productive language with great compiler diagnostics

    that scales from lowlevel to "just quick" programs.
  40. let counter = Counter { count: 0 }; for _

    in 1..3 { std::thread::spawn(move || { increment(&mut counter); // capture of moved value: `counter` }); }
  41. use std::rc::Rc; let rc = Rc::new(Counter { count: 0 });

    for _ in 1..3 { let handle = rc.clone(); std::thread::spawn(move || { // `std::rc::Rc<Counter>` cannot be sent between threads safely increment(&mut handle); }); }
  42. use std::sync::{Arc,Mutex}; let rc = Arc::new(Mutex::new(Counter { count: 0 }));

    for _ in 1..3 { let handle = rc.clone(); std::thread::spawn(move || { increment(&mut handle); }); }