presentation-rust-introduction/rust-intro.org

28 KiB

∈cludesvg[height=.25\textheight]{img/rust-logo-blk}≠wline An introduction to Rust

Rust

Abstract

#+LaTeX:∈cludegraphics[width = 0.5\textwidth]{img/Bruine_roest_op_tarwe_(Puccinia_recondita_f.sp._tritici_on_Triticum_aestivum).jpg} 5

Rust, the language

  • First public appearance 2010
  • Rust is the coating closest to the bare metal
  • Memory safe without gcing, optional reference counting
  • Ownership, lifetimes, traits, functional paradigms, zero-cost and zero-size abstractions
  • A well designed language and ecosystem

Strengths and Weaknesses1

Things Rust does measurably really well (Strengths)

Strengths and Weaknesses2

Points you might run into (Weaknesses)

  • Steep learning curve8 compiler enforcing (esp. memory) rules that would be "best practices" elsewhere.
  • Missing Rust-native libs in some domains, target platforms (esp. embedded), IDE features.8
  • Longer compile times than "similar" code in other languages.89
  • No formal language specification, can prevent legal use in some domains (aviation, medical, …).10
  • Careless (use of unsafe in) libraries can secretly break safety guarantees.

Problems Rust tries to conquer

#+LaTeX:∈cludegraphics[width = 0.8\textwidth]{img/compiler_complaint.png} 11

  • Dangling pointers / memory safety
  • Iterator invalidation
  • Thread safety / concurreny
  • Segfaults
  • Error handling
  • Zero-cost and zero-size abstractions

Where to start?

First Steps

Compact "cheat sheets"

Where to continue?

Further Steps

  • Jon Gjengset (Considering Rust, Crust of Rust, Advanced topics in Rust)

Where to share, find help and stay up to date

Language

  • Semicolons have meaning (statements vs. expressions)
  • Mutability
  • Shadowing
  • Memory safety
  • Ownership, references and lifetimes
  • Functional paradigms
  • Pattern matching
  • Metaprogramming (declarative and procedural macros)
  • Meaningful compiler warnings and errors

Statements vs. expressions

Statements

let something = true;
fn_call();

Expressions

(), false, x + y, fn_call(), if, match

Example

let bool_value = true;
let value = if bool_value {
    "true".to_string()
} else {
    "false".to_string()
};

Mutability and shadowing

Immutable variable

let foo = 5;

Mutable variable

let mut bar = 5;
bar = 6;

Shadowing

let baz = 5;
let baz = baz + 3;

Tuple structs

Definition of a tuple struct

struct Rectangle(usize, usize);

Instantiation of a tuple struct

let rect = Rectangle(3, 5);

Access of a tuple struct's field

rect.0;
rect.1;

Structs

  • Direct access vs. getter and setter (pub keyword)
  • Something::new(item) and Something::default()

Definition of a struct

struct Something {
    id: usize,
    item: String,
}

Enums

Enum example

enum Event {
    Receive,
    Send(String),
    Reset { address: u64, hard: bool },
}

let new_event = Event::Send("A message".to_owned());

Pattern matching and some macros

All match arms must return the same type!!1!

Simple match

match an_enum {
    MyEnum::Something => todo!("This has to be done"),
    MyEnum::SmthngElse(_) => unimplemented!("This is unimplemented"),
    MyEnum::AnotherThing(e) => println!("It is another thing: {}", e),
    _ => unreachable!(),
};

Assigning match

let maturity = match age {
    0 | 1 => "infant",
    2..=12 => "child",
    13..=19 => "teen",
    _ => "...",
};

if let

Simpler match

if let Some(inner_value) = foo {
    println!("Value: {}", inner_value);
} else {
    println!("Nothing found");
}

Loops3

Loops

  • loop: infinite loops
  • while: loops until a predicate is false
  • while let: loops until a pattern is matched
  • for: loop over an iterator until it is empty

Loop labels

  • break: terminate/exit a loop
  • continue: exit the current iteration and jump to the loop header

Ownership

Ownership is one of Rusts central concepts to achieve memory safety without garbage collection.

Ownership Rules

  • Each value has a variable that is called its owner.
  • There can only be one owner at a time.
  • When the owner goes out of scope, the value will be dropped.

References

  • A value can temporarily borrowed from its owner without transferring ownership.
  • There can be multiple immutable references.
  • There can only be one mutable reference.
  • The & symbol is the reference operator.
  • The * symbol is the dereference operator.
  • References are immutable by default, use the mut keyword to make them mutable.

Lifetimes

  • The Rust compiler (borrow checker) keeps track of how long references (borrows) are valid.
  • To ensure valid borrows and therefor prevent dangling borrows Lifetimes are used.
  • Lifetimes are annotations which tell the borrow checker how long a borrow needs to be valid.
  • Lifetimes are implicit and inferred, that means the compiler infers lifetimes wherever possible.
  • Lifetimes must be annotated when the borrow checker cannot infer them on its own.

Struct with lifetimes

struct SomeApp<'a> {
    config: &'a Config,
}

Error related types

  • Heavy use of Option<T> and Result<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()?;
...

Printing

When printing something, {} uses the std::fmt::Display trait and {:?} uses the std::fmt::Debug trait.

print!() and println!()

let amount = 3;
let fields = vec![0,2,5];

println!("We have {} of {:?}", amount, fields);

Prints: We have 3 of [0, 2, 5]

Deriving Debug

To enable printing of structs or enums when their fields implement Debug they can be derived with following annotation.

Deriving Debug

#[derive(Debug)]
struct Something {
    id: usize,
    item: String,
}

Implementing Display

Implementing Display

use std::fmt;

struct Something {
    id: usize,
    item: String,
}

impl fmt::Display for Something {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}: {}", self.id, self.item)
    }
}

Use functional paradigms

  • 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

Documentation

  • Documentation comments: ///
  • Markdown notation
  • cargo doc runns rustdoc which generates html docs
  • cargo doc --open runs rustdoc and opens the result in a browser
  • rustdoc documentation

FFI (Foreign Function Interface)

Cargo

Cargo

\Huge Cargo

/finga/presentation-rust-introduction/media/branch/main/img/cargo.png

What is it and how does it work?

  • Package manager
  • Build chain/tool
  • Cargo calls rustc, the Rust compiler
  • A Cargo project contains at least one crate
  • Custom build scripts with build.rs
  • Builds just work

Cargo commands

Create a new binary crate (init does so in an existing directory)

$ cargo new [--bin] <PATH>

Create a new library crate (init does so in an existing directory)

$ cargo new --lib <PATH>

Update build dependencies

$ cargo update

Install crates

$ cargo install [crate]

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!");
}

Docs

Cargo commands/plugins

What I typically use

  • audit: Audit Cargo.lock for security vulnerabilities.
  • bloat: Identify which dependency adds how much bloat.
  • checkmate: Run a list of checks (check, format, build, test, doc, audit).
  • clippy: Linter and static code analysis.
  • deb: Automatically create a Debian package.
  • flamegraph: Generate flamegraphs about anything.
  • fmt: Format Rust code according to style guidelines.
  • install-update: Keep all your Cargo commands/plugins up to date.

Cargo downloads your Rust package's dependencies, compiles your package, makes distributable packages, and is able to upload them to crates.io, the Rust community's package registry.

The Rust (package) registry

Official registry

Unofficial crates.io frontend

lib.rs same but different..

Setup

Setup

\Huge

How to get all that stuff?

Install the Rust-Toolchain

Install the Rust toolchain (rustup.rs)

$ curl --proto '=https' --tlsv1.2 -sSf \
  https://sh.rustup.rs | sh

Rustup: The Rust toolchain installer

  • Check for new versions: $ rustup check
  • Update the toolchain: $ rustup update
  • Update rustup itself: $ rustup self update
  • Install Rust nightly (daily builds): $ rustup toolchain install nightly
  • Activate nightly globally: $ rustup default nightly
  • Activate nightly locally (in current dir): $ rustup override set nightly

IDE support aka are we IDE yet?4

\tiny

\rotatebox{90}{Syntax highlightning (.rs)} \rotatebox{90}{Syntax highlightning (.toml)} \rotatebox{90}{Snippets} \rotatebox{90}{Code Completion} \rotatebox{90}{Linting} \rotatebox{90}{Code Formatting} \rotatebox{90}{Go-to Definiton} \rotatebox{90}{Debugging} \rotatebox{90}{Documentation Tooltips}
Atom X X X X X X X X
Emacs X X X X X X X X
Sublime X X X X X X X
Vim/Neovim X X X X X X X X
VS Code X X X X X X X X X
BBedit X X X X X
Geany X
gedit X X X
Kakoune X X X X X X X X
Kate X X X X X X
Micro X X X X
Midnight Commander X
Textadept X X X X X X
Eclipse X X X X X X X X
IntelliJ-based IDEs X X X X X X X X X
Visual Studio X X X X
GNOME Builder X X X X X X
Ride X

Common Crates

General

Misc. Crates

Parsing/Encoding

  • clap: The command line argument parser (use v3.0.0-beta when starting a new project)
  • serde
  • nom
  • pest

Error handling

  • anyhow
  • thiserror
  • snafu
  • easy-error
  • eyre
  • color-eyre

Logging

  • log: a lightweight logging facade
  • env_logger
  • simple_logger
  • simplelog
  • pretty_env_logger
  • stderrlog
  • flexi_logger
  • log4rs
  • fern
  • and many many more (checkout the log readme12)

Testing

Data handling

  • lazy_static
  • once_cell (globality, singleton, lazy initialization)
  • static_assertions
  • GhostCell

Databases

Tui

  • rustyline: Line editing library
  • tui: Library for ncurses like things
  • crossterm: Cross-platform terminal manipulation library

Gui (areweguiyet.com)

A small selection of crates I used/tried so far:

WebDev

Unikernel

For general information about unikernels visit unikernel.org.

Thats it for now

\Large

Thank you for your attention!

Questions?

\normalsize

To be continued

Column left
  • Traits
  • Generics
  • Type Aliases vs. New Type Idioms
  • Ownership
  • References and Slices
  • Lifetimes
  • Iterators
  • Functional paradigms
Column right
  • Metaprogramming (macros)
  • (Smart) Pointers
  • Concurrency
  • Object Oriented Programming
  • Closures
  • Async-Await
  • unsafe Rust

Footnotes