An Introduction to Rust

Disclaimer

I’m not actually a Rust developer!

 

 

There is a lot to cover.

If you get bored or overwhelmed, just yet at me!
 

The order of the slides may be confusing; its just hard to prioritize and there is just so much I would like to talk about.

History & Goals

  • Developed by Mozilla to solve issues with usage of C++
  • First announced at the Mozilla Summit 2010
    (I was there :-)
  • Used to have a GC, and a runtime around libuv, and strange sigils for different pointer types
     
  • Hit 1.0 in May, still evolving and stabilizing features
    (you basically have to use the nightly for the cool stuff

Basics

  • a very low level systems programming language with a lot of high level concepts
  • you can use it to develop kernels (some do!)
  • compiled; bare-metal performance
  • zero-cost abstractions
  • curly-brace syntax
  • blocks as expressions
  • focused on (memory) safety, speed and concurrency
     
  • C FFI!
    • you can use any C library
    • you can use it to write libraries for any language that can use C-like libraries
    • (some use it for ruby/python/node modules)

Cargo

  • Official Package Manager / Build Tool
  • makes it really easy to create, manage, build, run and test your *crate* (that’s what rust packages are called)
  • https://crates.io/ is the official crate repository
  • just reference any crate via semver and cargo will download, compile and link it to your crate
  • you can also compile and install binaries locally

borrowck

  • really simple in theory:
    • you have one variable on the stack that owns a val
    • you can transfer ownership (move)
    • you can lend out (borrow) references:
      • any number of read-only references
        *OR*
      • one mutable reference
  • think of modern C++ with move semantics
     
  • unsafe code can bend the rules
    to create safe abstractions!

------

borrowck

borrowck pitfalls

  • you have to think *a lot* about allocations etc
    which is a good thing!
    but a really annoying nontheless
     
  • (sorry, no example)
    a lot of times you have to introduce variable bindings because of borrowcks stupidity.
    it simply won’t let you write one-liners sometimes, for no good reason
     
  • there is an RFC for that: https://github.com/rust-lang/rfcs/issues/811
// Hello Rust!

fn main() {
  println!("Hello Rust!");
}

Macros

  • rust supports different kinds of syntax extensions
  • macros or compiler plugins (only for nightly users)
  • The "hello rust" example actually contains a macro!

Data Types

  • Primitives, Tuples, Arrays (strings) (more later), Structs, Enums
  • Pointers/References (more later)
     
  • u8/i8, u16/i16, u32/i32, u64/i64, f32, f64
struct Foo {
    foo: (u8, u8),
    bar: u16,
}

Enums

  • Union Types, Sum Types, Algebraic Data Types
  • throw in a generic type parameter and you have a GADT (generalized ADT)
pub enum Option<T> {
    None,
    Some(T),
}
  • Option is like Maybe in Haskell
  • There’s also Result<T, E> { Ok(T), Err(E) }
    like Either in Haskell
     
  • there are no null pointers in (safe) rust, you use Option<T> instead

Match

impl<T> Option<T> {
    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
        match self {
            Some(x) => Some(f(x)),
            None => None,
        }
    }
}
  • you *must* explicitly match enums to use their contents!
  • this makes error and None("null-pointer") handling explicit
  • If you really don’t want to: Option and Result offer .unwrap()/.expect()

    which basically means: just abort the program if I’m wrong, I don’t care!
     
  • There is also try!() for Result

Traits

  • they are an extremely powerful mechanism to add functionality to any type of data structure
  • More powerful than classical Interfaces, a bit like Typeclasses
pub trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { ... }
    fn read_to_string(&mut self, buf: &mut String) -> Result<usize> { ... }
    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { ... }
    fn by_ref(&mut self) -> &mut Self where Self: Sized { ... }
    fn bytes(self) -> Bytes<Self> where Self: Sized { ... }
    fn chars(self) -> Chars<Self> where Self: Sized { ... }
    fn chain<R: Read>(self, next: R) -> Chain<Self, R> where Self: Sized { ... }
    fn take(self, limit: u64) -> Take<Self> where Self: Sized { ... }
    fn tee<W: Write>(self, out: W) -> Tee<Self, W> where Self: Sized { ... }
}

impl Read for Foo {
    fn read(&mut self, but: &mut [u8]) -> Result<usize> {
        // …
    }
}

Method Overloading

trait Foo {
	fn foo(&self) -> u32 {
		1
	}
}

struct Bar<T> {
	bar: T,
}

impl Foo for u32 {}
impl<'a> Foo for &'a str {}
impl<T> Foo for Bar<T> {}

fn foo<T>(arg: T) -> u32 where T: Foo {
	arg.foo()
}

fn main() {
	println!("{}", foo(10));
	println!("{}", foo("foo"));
	println!("{}", foo(Bar{bar: Bar{bar: "bar"}}));
}

Operator Overloading

use std::ops::Add;

#[derive(Copy, Clone)]
struct Foo;

impl Add for Foo {
    type Output = Foo;

    fn add(self, _rhs: Foo) -> Foo {
        println!("Adding!");
        self
    }
}

fn main() {
    Foo + Foo;
}

How does thread safety work?

  • there are two "marker" Traits: Send and Sync
  • Send means the Type can be safely sent between threads (opt-out)
  • Sync means that a type guarantees thread safety
  • T: Sync implies &T: Send, in english:
    if a type guarantees thread safety, you can send multiple (read only) references to different threads
impl Send for Arc<T>
impl !Send for Rc<T>

impl Sync for Mutex<T>

DST

  • Dynamically Sized Types
     
  • the compiler knows the size (in bytes) of some types at compile time: primitives, structs, enums, fixed-size arrays. Rust can use thin pointers to reference those
     
  • others are only known at runtime: DSTs.
    Rust uses fat pointers
    (pointer + length / pointer + vtable) for
    slices ( &[T] ) and Trait Objects ( &Trait ) respectively
     
  • &str vs String

Static / Dynamic Dispatch

fn<T: Bar> foo(bar: &T) {
    // …
}

// vs

fn foo(bar: &Bar) {
    // …
}

Closing remarks

  • I would love to actually do more Rust
     
  • What I miss most in other languages (JS) since I tried Rust:
  • ADTs, Traits, zero-cost-abstractions, and a lot more
     
  • go on and read the awesome rust book!

An Introduction to Rust

By Arpad Borsos

An Introduction to Rust

  • 1,022
Loading comments...

More from Arpad Borsos