An Introduction to Rust

 

By Sunjay Varma

twitter.com/sunjay03

github.com/sunjay

Talk Outline

  • What is Rust?
  • A Practical Introduction to Rust
  • Advanced Features
  • Concluding remarks & Resources

What is Rust?

Rust is a safe, productive, high-performance, and low-level programming language.

Rust is safe

  • Automatic memory management without garbage collection
  • Ownership and Borrowing
  • Memory safe and with no data races!
  • No more double frees, use-after-frees, dangling pointers, out-of-bounds accesses, or segfaults!
  • Multithreaded programming without fear!

Rust is productive

  • You can start writing production ready code in Rust today.

Rust is productive

  • You can start writing production ready code in Rust today.
  • Rust is designed to be a language that you actually use to write applications -- more than just an obscure research language
  • The compiler can check a lot about your code. Just compiling a Rust program means that you have already avoiding a lot of potential bugs
  • Rust gives you a lot of confidence to change and refactor your program, even after time has passed, because it checks all of those things every time

Rust is high-performance

  • Just like C or C++, Rust gives you complete control
  • Compile-time safety checks, correctness checks, and memory management decisions for zero runtime impact
  • Automatic memory management without garbage collection
  • Zero-cost abstractions so your code runs fast while being fun to write as well

Rust is low-level

  • Low-level without some of the worst parts of being a low-level language
  • Complete control without unnecessary burden
  • unsafe allows you to tell the Rust compiler that you know better!

let mut num = 5;

let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;

unsafe {
    // Dereferencing raw pointers can be unsafe!
    println!("r1 is: {}", *r1);
    println!("r2 is: {}", *r2);
}

Example from the Rust book: Chapter 19 - Unsafe Rust

Rust is a safe, productive, high-performance, and low-level programming language.

A Practical Introduction to Rust

Tic-Tac-Toe

Variables

let x = 12;
let y = "Hello, world!";

// Type annotations are optional
let x: i32 = 12; // i32 = 32-bit integer
let y: &str = "Hello, world!"; // &str = string

Functions

// Though type annotations are not often required,
// functions must always be explicit about their types
fn max(x: i32, y: i32) -> i32 {
    if x >= y {
        x
    }
    else {
        y
    }
}

Common Rust Types

  • Unsigned Integers: u8, u16, u32, u64, usize
  • Signed Integers: i8, i16, i32, i64, isize
  • Floating-point: f32, f64
  • Boolean: bool (true and false)
  • Character: char
  • Strings: &str, or String (heap-allocated)
  • Unit: ()
  • Compound Types: Option<u32>, (i32, f64, &str), [i32; 5]

Creating a new project

To install Rust, go to: https://rustup.rs

Structs

struct Game {
    tiles: [[Option<Piece>; 3]; 3], // Syntax: [Type; size]
    current_piece: Piece,
    winner: Option<Winner>,
}

Enums

enum Piece {
    // Access these variants using `Piece::X` or `Piece::O`
    X,
    O,
}

enum Winner {
    X,
    O,
    Tie,
}

Option

enum Option<T> {
    Some(T),
    None,
}

Result

enum Result<T, E> {
    Ok(T),
    Err(E),
}

For when there may or may not be a value

For when an operation might fail

Example: Result

// Takes a string (e.g. "1c") and converts it into its position on the board (e.g. (0, 2))
fn parse_move(input: &str) -> Result<(usize, usize), InvalidMove> {
    if input.len() != 2 {
        return Err(InvalidMove(input.to_string()));
    }

    let row = match &input[0..1] {
        "1" => 0,
        "2" => 1,
        "3" => 2,
        _ => return Err(InvalidMove(input.to_string())),
    };

    let col = match &input[1..2] {
        "A" | "a" => 0,
        "B" | "b" => 1,
        "C" | "c" => 2,
        invalid => return Err(InvalidMove(invalid.to_string())),
    };

    Ok((row, col))
}

struct InvalidMove(String);

(0, 2)

Ownership & Borrowing

The key to memory safety and no more data races

Ownership

  1. Each value in Rust has a variable that's called its owner.
  2. There can only be one owner at a time.
  3. When the owner goes out of scope, the value will be dropped.

Borrowing

let x = "Hello, world!".to_string();
do_something(x);
println!("{}", x);

fn do_something(x: String) {
    // ...
}

x is moved

let x = "Hello, world!".to_string();
do_something(&x);
println!("{}", x);

fn do_something(x: &String) {
    // ...
}

x is borrowed

error[E0382]: use of moved value: `x`
 --> src/main.rs:4:16
  |
2 | do_something(x);
  |              - value moved here
3 | println!("{}", x);
  |                ^ value used here after move
  |

It works!

moved to preserve "one owner" rule

References and Borrowing

`&` vs. `&mut`

  1. At any given time, you can have either but not both of:
    • One mutable reference
    • Any number of immutable references
  2. References must always be valid

Immutable Reference

(cannot modify anything)

Mutable Reference

(can modify things)

Advanced Features: Generics & Traits

Generics

fn add<T>(x: T, y: T) -> T {
    x + y
}

For when your code works for a variety of types

error[E0369]: binary operation `+` cannot be applied to type `T`
 --> src/main.rs:2:5
  |
2 |     x + y
  |     ^^^^^
  |
  = note: `T` might need a bound for `std::ops::Add`
fn add<T: Add>(x: T, y: T) -> T {
    x + y
}

Traits

For when you need to say more about your generics 

* This example won't actually exactly compile as-is, but it is a good enough demonstration of the concept for our purposes.

Tic-Tac-Toe

Rust Resources

  • Learn Rust from scratch by drawing pictures
  • No prior programming experience required
  • Ready in a few weeks!

Turtle

Website: turtle.rs

Thank you!

Made with Slides.com