# About me * Been programming since April, 1984 * BASIC, Pascal, a lot of C, a little C++, various Unix shells, AWK, a taste of FORTRAN77, a smidgen of Perl, a lot of Python, a bit of Lisp, a little Haskell, a little Go, and now Rust * I really like Rust ----------------------------------------------------------------------------- # About Rust * systems programming * emphasis on safety, performance * a bit like C and Haskell had a baby * Cargo build tool and dependency manager * long-term stability: editions * a lively ecosystem of libraries ("crates") ----------------------------------------------------------------------------- # Why I like Rust: strong, static, versatile typing * prevents entire classes of bugs: NULL pointer errors, unchecked error returns, implicit but wrong value conversions, data races * "if a program compiles, it usually works" (not really) ----------------------------------------------------------------------------- # Why I like Rust: type inference * less typing, more power * mostly only need to specify function signatures, structs, and similar parts and the compiler deduces the rest * also infers lifetimes when it's unambiguous ----------------------------------------------------------------------------- # 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, or use after free), hard to debug * automatic, with garbage collection - Lisp, Python, Go, Java, ... - 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 - compiler can prove lack of data races - bonus: mutability must be declared explicitly ----------------------------------------------------------------------------- # Why I like Rust: random bits * not controlled by a mega-corporation * the `?` operator and the `Result` type * the `Option` type * enums are powerful, not just names for integers * 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, ecosystem, community * `cargo` works well and avoids most pitfalls other language package managers have * culture of good documentation, with doctests * things keep getting better, without breaking things * avoids the 20/80 principle: problems are solved thoroughly - culture avoids "pragmatic shortcuts" to allow implementation to be simpler, if the shortcut would be likely to bite users * the community is friendly and constructive and lively ----------------------------------------------------------------------------- # Not everything is perfect: Rust is not finished * language and libraries change - rustup install/update preferred over distro packages - new language release every six weeks * fairly young - Rust: 2010 (1.0 in 2015) - Go: 2009 - C#: 2000 - PHP, Java, JavaScript: 1995 - Python: 1991 - Haskell: 1990 - Perl: 1987 - C: 1972 - Lisp: 1958 - Fortran: 1957 - Plankalkül: 1948 ----------------------------------------------------------------------------- # Not everything is perfect: portability, etc * supports a fraction of the targets of C - but all the big mainstream ones are supported * de facto static linking only - dynamic linking works, but Rust ABI is not yet stable, so static is the default * compilation is slow ----------------------------------------------------------------------------- # Example: hello.rs (self-standing) ```{.rust .numberLines} fn main() { println!("Hello, world!"); } ``` Compiled with the Rust compiler directly: ~~~{.sh .numberLines} $ rustc hello.rs $ ./hello Hello, world! $ ~~~ ----------------------------------------------------------------------------- # Example: hello with Cargo * `cargo` is the Rust build tool and dependency manager * `cargo init` * minimum files * `Cargo.toml` * `src/main.rs` ----------------------------------------------------------------------------- # Example: hello with Cargo: `Cargo.toml` ~~~{.toml .numberLines} [package] name = "hello" version = "0.1.0" authors = ["Lars Wirzenius "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] ~~~ ----------------------------------------------------------------------------- # Example: hello with Cargo: `src/main.rs` ~~~{.rust .numberLines} fn main() { println!("Hello, world!"); } ~~~ ----------------------------------------------------------------------------- # Example: hello with Cargo: commands ~~~{.sh .numberLines} $ cargo init --bin hello $ cd hello $ cargo run Compiling hello v0.1.0 (/home/liw/wmf/talks/x/hello) Finished dev [unoptimized + debuginfo] target(s) in 0.37s Running `target/debug/hello` Hello, world! $ cargo run -q Hello, world! $ ~~~ ----------------------------------------------------------------------------- # Long example: summain A real program. Outputs a _manifest_ with metadata about files named on the command line. Testing tool for backup software. Uses concurrency (threads) for speed. \ Actual source code: [`https://gitlab.com/larswirzenius/summain`](https://gitlab.com/larswirzenius/summain) ----------------------------------------------------------------------------- # Summain output, sample ~~~{.yaml .numberLines} --- path: "./COPYING" mode: "-rw-rw-r--" mtime: 1613482516 mtime_nsec: 369325267 nlink: 1 size: 35147 sha256: 8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903 target: ~ --- path: src mode: drwxrwxr-x mtime: 1613482516 mtime_nsec: 369325267 nlink: 3 size: ~ sha256: ~ target: ~ ~~~ ----------------------------------------------------------------------------- # Example: summain: main program, part 1 ~~~{.rust .numberLines} fn main() -> anyhow::Result<()> { let mut opt = Opt::from_args(); opt.pathnames[..].sort(); let v: Vec> = opt.pathnames .par_iter().map(|p| manifest(&p)) .collect(); for m in v { let m = m?; println!("{}", serde_yaml::to_string(&m)?); } Ok(()) } ~~~ ----------------------------------------------------------------------------- # Example: summain: main program, part 2 ~~~{.rust .numberLines} #[derive(StructOpt, Debug)] struct Opt { #[structopt(parse(from_os_str))] pathnames: Vec, } fn manifest( path: &Path ) -> anyhow::Result { ManifestEntry::new(path) .with_context(|| format!("{}", path.display())) } ~~~ ----------------------------------------------------------------------------- Example: summain: `struct ManifestEntry` ~~~{.rust .numberLines} #[derive(Serialize, Debug)] pub struct ManifestEntry { path: String, #[serde(with = "mode")] mode: u32, mtime: i64, mtime_nsec: i64, nlink: u64, size: Option, sha256: Option, target: Option, } ~~~ ----------------------------------------------------------------------------- # Example: summain: `impl ManifestEntry`, part 1 ~~~{.rust .numberLines} impl ManifestEntry { pub fn new(path: &Path) -> std::io::Result { let m = symlink_metadata(path)?; let hash = if m.is_file() { Some(file_checksum(path)?) } else { None }; let target = if m.file_type().is_symlink() { Some(read_link(path)?) } else { None }; ~~~ ----------------------------------------------------------------------------- # Example: summain: `impl ManifestEntry`, part 2 ~~~{.rust .numberLines} Ok(Self { path: path.to_string_lossy().into_owned(), mode: m.st_mode(), mtime: m.st_mtime(), mtime_nsec: m.st_mtime_nsec(), nlink: m.st_nlink(), size: if m.is_dir() { None } else { Some(m.st_size()) }, sha256: hash, target, }) } } ~~~ ----------------------------------------------------------------------------- # Example: summain: `file_checksum` ~~~{.rust .numberLines} fn file_checksum(path: &Path) -> std::io::Result { let mut hasher = Sha256::new(); let file = File::open(path)?; let mut reader = BufReader::new(file); let mut buf = vec![0; BUF_SIZE]; loop { let n = reader.read(&mut buf)?; if n == 0 { break; } hasher.update(&buf[..n]); } let hash = hasher.finalize(); Ok(format!("{:x}", hash)) } ~~~ ----------------------------------------------------------------------------- # Example: summain: `mod mode` ~~~{.rust .numberLines} mod mode { use serde::{self, Serializer}; pub fn serialize( mode: &u32, serializer: S ) -> Result where S: Serializer, { let s = unix_mode::to_string(*mode); serializer.serialize_str(&s) } } ~~~ ----------------------------------------------------------------------------- ![](htop-summain.png) ----------------------------------------------------------------------------- # SEE ALSO **Rust** [`https://www.rust-lang.org/`](https://www.rust-lang.org/) **crates.io** [`https://crates.io/`](https://crates.io/) **Summain** [`https://gitlab.com/larswirzenius/summain`](https://gitlab.com/larswirzenius/summain) **Subplot** [`https://gitlab.com/larswirzenius/subplot/`](https://gitlab.com/larswirzenius/subplot/) **These slides:** [`http://git.liw.fi/wmf-talks`](http://git.liw.fi/wmf-talks) ----------------------------------------------------------------------------- # Legalese Copyright 2021 Wikimedia Foundation This content is licensed under the Creative Commons Attribution-ShareAlike 4.0 International ([CC BY-SA 4.0][]) licence. [CC BY-SA 4.0]: https://creativecommons.org/licenses/by-sa/4.0/ --- title: "Why Rust?" subtitle: "A brief introduction" author: Lars Wirzenius date: 2021-02-16 ...