From d3ee74da2976b46e198666d20a975a910e9de9bb Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Wed, 18 Mar 2020 14:53:54 +0200 Subject: Add: why rust talk --- why-rust.md | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 why-rust.md diff --git a/why-rust.md b/why-rust.md new file mode 100644 index 0000000..b166f03 --- /dev/null +++ b/why-rust.md @@ -0,0 +1,236 @@ +--- +title: "Why Rust?" +author: Lars Wirzenius +... + +# About me + +* First learnt programming in April, 1984, so 36 years ago +* Been programming more or less daily ever since +* BASIC, Pascal, C, a little C++, Unix shell, AWK, a lot of Python, Lisp, a + little Haskell, a little Go, and now Rust +* I really like Rust +* Rust is like Haskell and C had a love child with unicorn fairy godmothers + +--- + +# Why I like Rust: type system + +* strong, static, versatile typing + + - helps avoid many bugs, reduces need to test every statement and + branch with unit tests + - not quite true that if a program compiles, it works, but Rust is a + lot closer to that than C, C++, Pascal, etc + - avoids entire classes of bugs: NULL pointer errors, unchecked + error returns, implicit but wrong value conversions + +* type inference: mostly only need to specify function signatures, + structs, and similar parts and the compiler deduces the rest + +--- + +# Why I like Rust: memory management + +* manual, a la C: malloc, free + + - error prone: easy to leak (no free), easy to break (more than one + free), hard to debug + +* automatic, with garbage collection + + - Lisp, Python, Go + - requires a runtime + - despite decades of research, inevitably there is overhead and + short pauses + +* Rust: automatic with borrow checker, lifetimes + + - all heap values heap are tied to values on the stack + - when on-stack value is freed, the heap value is dropped + - requires making sure stack values have the right lifetimes + - mutability must be declared explicitly + - compiler can prove lack of data races + +--- + +# Why I like Rust: misc + +* not controlled by a megacorporation +* the ? operator and the Result type +* the Option type +* enums are powerful +* pattern matching is powerful and exhaustive +* no OOP, but traits +* zero cost abstractions actually work +* binaries execute quickly +* excellent concurrency support + +--- + +# Why I like Rust: tooling and ecosystem + +* cargo, crates.io work well and avoid most pitfalls other languages + have +* good doctests +* things get better, the community and ecosystem seem lively +* avoids the 20/80 principle: problems are solved well, "pragmatic + shortcuts" to allow implementation to be simpler are avoided, if the + shortcut would be likely to bit users +* the community is friendly and constructive + +--- + +# Not everything is perfect + +* things in the language and important libraries change + - but almost always with backwards compatibility) +* supports a lot fewer targets than C + - but the big mainstream ones are supported +* not well packaged for Linux distros; rustup is needed + - bug as the language stabilizes, this will improve +* de facto static linking only + - dynamic linking works, but Rust ABI is not yet stable, so static + is the default +* compilation is somewhat slow + +--- + +# Example: hello + +```{.rust .numberLines} +fn main() { + println!("Hello, world!"); +} +``` + +--- + +# Example: Subplot docgen main program + +```{.rust .numberLines} +use chrono::{Local, TimeZone}; +use pandoc; +use std::fs; +use std::path::Path; +use std::path::PathBuf; +use std::time::UNIX_EPOCH; +use structopt::StructOpt; + +use subplot; +``` +--- + +```{.rust .numberLines} +// Define the command line arguments. +#[derive(Debug, StructOpt)] +#[structopt(name = "docgen", + about = "Subplot document generator.")] +struct Opt { + // One or more input filename. + #[structopt(parse(from_os_str))] + filenames: Vec, + + // Set output file name. + #[structopt(name = "FILE", long = "--output", + short = "-o", parse(from_os_str))] + output: PathBuf, + + // Set date. + #[structopt(name = "DATE", long = "--date")] + date: Option, +} +``` +--- + +```{.rust .numberLines} +fn main() -> subplot::Result<()> { + let opt = Opt::from_args(); + let mut pandoc = pandoc::new(); + + // Tell Pandoc what the input files are. + let first_file = &opt.filenames[0]; + for filename in opt.filenames.iter() { + pandoc.add_input(&filename); + } +``` +--- + +```{.rust .numberLines} + // Tell Pandoc what the input file format is. + pandoc.set_input_format( + pandoc::InputFormat::Markdown, + vec![pandoc::MarkdownExtension::Citations], + ); + + // Add external Pandoc filters. + let citeproc = std::path::Path::new("pandoc-citeproc"); + pandoc.add_option( + pandoc::PandocOption::Filter( + citeproc.to_path_buf())); + + // This would be nicer if pandoc took a Pathbuf for output, + // instead of a String. Reported as + // + let output = format!("{}", opt.output.display()); + pandoc.set_output(pandoc::OutputKind::File(output)); +``` +--- + +```{.rust .numberLines} + // Set options for Pandoc. + pandoc.add_option(pandoc::PandocOption::TableOfContents); + pandoc.add_option(pandoc::PandocOption::Standalone); + pandoc.add_option(pandoc::PandocOption::NumberSections); +``` +--- + +```{.rust .numberLines} + // Metadata date from command line or file mtime. However, we + // can't set it directly, since we don't want to override the date + // in the actual document, if given, so we only set + // user-provided-date. Our parsing code will use that if date is + // not document metadata. + let date = if let Some(date) = opt.date { + date + } else { + // We have to parse the document to get the date from the + // metadata. We can't set the metadata for typesetting until + // we do. So we parse it twice. + let doc = subplot::Document::from_file(&first_file)?; + if let Some(date) = doc.meta().date() { + date.to_string() + } else { + mtime(first_file)? + } + }; + pandoc.add_option(pandoc::PandocOption::Meta("date".to_string(), Some(date))); +``` +--- + +```{.rust .numberLines} + // Run Pandoc to parse the inputs into an abstract syntax tree, + // then run our filter on that, then let Pandoc produce the output + // file from the AST. + pandoc.add_filter(|json| { + let mut doc = subplot::Document::from_json(&json).expect("error parsing JSON AST"); + doc.has_title().expect("document has no title"); + doc.typeset(); + doc.ast().expect("error serializing into JSON AST") + }); + pandoc.execute()?; + + Ok(()) +} +``` +--- + +```{.rust .numberLines} +fn mtime(filename: &Path) -> subplot::Result { + let mtime = fs::metadata(filename)?.modified()?; + let secs = mtime.duration_since(UNIX_EPOCH)?.as_secs(); + let secs: i64 = format!("{}", secs).parse()?; + let dt = Local.timestamp(secs, 0); + Ok(dt.format("%Y-%m-%d %H:%M").to_string()) +} +``` -- cgit v1.2.1