+ - 0:00:00
Notes for current slide

Introduce yourself, ask TAs to introduce if they're there.

Notes for next slide

Like I'll explain a little later, this class is run almost entirely through GitHub, so you can actually access the original/Markdown view of the slides -- say, if you wanted to search a lecture for something. Otherwise, the rendered view of the slides is on the website.

CIS 198: Rust Programming

1 / 65

Introduce yourself, ask TAs to introduce if they're there.

Lecture 00: Hello, Rust!

This lecture online:
GitHub One-Page ViewSlide View
2 / 65

Like I'll explain a little later, this class is run almost entirely through GitHub, so you can actually access the original/Markdown view of the slides -- say, if you wanted to search a lecture for something. Otherwise, the rendered view of the slides is on the website.

Overview

"Rust is a systems programming language that runs blazingly fast, prevents nearly all segfaults, and guarantees thread safety."

rust-lang.org

  • But what does that even mean?
3 / 65

Blazingly Fast

  • Speed comparable to C/C++
  • Rust compiles to native code
  • Has no garbage collector
  • Many abstractions have zero cost
  • Fine-grained control over lots of things
  • Pay for exactly what you need...
  • ...and pay for most of it at compile time
4 / 65

Prevents Nearly All Segfaults

  • No null
  • No uninitialized memory
  • No dangling pointers
  • No double free errors
  • No manual memory management!
5 / 65

Guarantees Thread Safety

  • Rust does not allow shared, mutable data
  • Mutexes (and other atomics)
  • Compiler rules for shared data (Send and Sync)
6 / 65

Functional

  • Algebraic datatypes (sum types / enum types)
  • Pattern matching
  • First-class functions
  • Trait-based generics (interfaces)
7 / 65

Zero-Cost 100% Safe Abstractions

  • Strict compile-time analysis removes need for runtime
  • Big concept: data ownership & borrowing
  • Other things like traits, generics, lifetimes, etc.
8 / 65

Do What You Want

  • You can write unsafe code to get circumvent... most things.
9 / 65

Release Model: Trains

  • Rust has a new stable release every six weeks
  • Nightly builds are available, well, nightly
  • Current stable: Rust 1.11
  • Train model:
Date Stable Beta Nightly
2016-08-25 🚂 1.11 🚆 1.12 🚝 1.13
2016-09-29 🚆 1.12 🚝 1.13 🚃 1.14
2016-11-10 🚝 1.13 🚃 1.14 🚋 1.15
2016-12-22 🚃 1.14 🚋 1.15 🚅 1.16
10 / 65

Development

11 / 65

Who Uses Rust?

  • Mozilla: Firefox, Servo
    • Firefox shipped a Rust mp4 track metadata parser last summer.
  • Skylight
  • Dropbox
  • Maidsafe
  • OpenDnS
  • wit.ai (Facebook)
  • Codius
12 / 65

Some Big Rust Projects

13 / 65

Administrivia

  • 8 homeworks (50%), final project (40%)
  • Participation (10%)
  • Weekly Rust lecture: Tue. 4:30-6:00pm, Towne 321
  • Mini-Course lecture: Tue. 6:00-7:30pm, Towne 100
  • Piazza
    • We will be using Piazza for announcements; make sure you have gotten emails!
  • Consult the website for the schedule, slides, and homework.
  • Class source material hosted on GitHub.
    • Corrections welcome via pull request/issue!
  • Course is in development - give us feedback!
14 / 65

Administrivia: Homeworks (50%)

  • 8 homeworks.
  • Released each Tuesday and (usually) due the following Wednesday night at midnight.
  • We will be using GitHub Classroom.
    • Click the link to make a private repo for every homework, which will be your submission.
  • You get 5 late days (24 hours each). You may use up to 2 late days on an assignment.
    • 20% penalty per day after 2 late days or if you're out of late days.
  • Make sure your code is visible on GitHub at midnight.
    • Late days applied automatically if you push to your repo after the due date. (Do let us know if you would like to use your initial submission instead.)
15 / 65

Prerequisites

  • CIS240 is suggested.
  • Not a hard limit, although you may struggle with references if you haven't taken 240.
  • Familiarity with Git and using the command line.
    • There are links on hw0 about both of these things.
16 / 65

Administrivia: Office Hours

  • Monday, Tuesday, Wednesdays

Office hours held in the Levine 6th floor lounge.

  • Any changes will be announced.
  • Check the website or Google calendar for the up-to-date schedule.
17 / 65
18 / 65

Let's Dive In!

Hello, Rust!

fn main() {
println!("Hello, CIS 198!");
}
  • All code blocks have links to the Rust playpen so you can run them!
19 / 65

Basic Rust Syntax

20 / 65

Variable Bindings

  • Variables are bound with let:

    let x = 17;
  • Bindings are implicitly-typed: the compiler infers based on context.

  • If the compiler can't determine the type of a variable, so sometimes you have to add type annotations.

    let x: i16 = 17;
  • Variables are immutable by default:

    let x = 5;
    x += 1; // error: re-assignment of immutable variable x
    let mut y = 5;
    y += 1; // OK!
21 / 65

Variable Bindings

  • Bindings may be shadowed:

    let x = 17;
    let y = 53;
    let x = "Shadowed!";
    // x is not mutable, but we're able to re-bind it
  • The first binding for x is shadowed until the second binding goes out of scope.

let x = 17;
let y = 53;
{
let x = "Shadowed!";
} // This second binding goes out of scope
println!("{}", x); // ==> 17
22 / 65

Functions

  • Functions are declared with fn.
  • Return types are indicated with ->.
  • Arguments are written var: type.
fn foo(x: u32) -> i32 {
// ...
}
  • Functions are called with parens:
    foo(42);
23 / 65

Functions

  • Must explicitly define argument and return types.
    • The compiler could probably figure this out, but Rust's designers decided it was better practice to force explicit function typing.
24 / 65

More self-documenting and provides another layer of protection against bugs.

Functions

  • The final expression in a function is its return value.
    • Use return for early returns from a function.
fn square(n: i32) -> i32 {
n * n
}
fn squareish(n: i32) -> i32 {
if n < 5 { return n; }
n * n
}
fn square_bad(n: i32) -> i32 {
n * n;
}
  • The last one won't even compile!
    • Why? It ends in a semicolon, so it evaluates to ().
25 / 65

Unit

  • The "nothing" type is called "unit", which is written ().
    • The type () has only one value: ().
26 / 65

Expressions

  • (Almost!) everything is an expression: something which evaluates to a value.
    • Exception: variable bindings are not expressions.
  • () is the default return type.
  • Discard an expression's value by appending a semicolon. Now it evaluates to ().
    • Hence, if a function ends in a semicolon, it returns ().
fn foo() -> i32 { 5 }
fn bar() -> () { () }
fn baz() -> () { 5; }
fn qux() { 5; }
27 / 65

Expressions

  • Because everything is an expression, we can bind many things to variable names:

    let x = -5;
    let y = if x > 0 { "greater" } else { "less" };
    println!("x = {} is {} than zero", x, y);
  • Aside: "{}" is Rust's basic string interpolation operator

    • Similar to Python, Ruby, C#, and others; like printf's "%s" in C/C++.
28 / 65

Comments

/// Triple-slash comments are docstring comments.
///
/// `rustdoc` uses docstring comments to generate
/// documentation, and supports **Markdown** formatting.
fn foo() {
// Double-slash comments are normal.
/* Block comments
* also exist /* and can be nested! */
*/
}
29 / 65

Even if the parser for this syntax highlighter doesn't believe me.

Types

30 / 65

Primitive Types

  • bool: true and false.
  • char: 'c' or '😺' (chars are Unicode!).

  • Numeric types: specify the signedness and bit size.

    • i8, i16, i32, i64, isize
    • u8, u16, u32, u64, usize
    • f32, f64
    • isize & usize are the size of pointers (and therefore have machine-dependent size)
    • Literals can have a type suffix: 10i8, 10u16, 10.0f32, 10usize.
    • Otherwise type inference defaults to i32 or f64:
      • e.g. 10 defaults to i32, 10.0 defaults to f64.
  • Arrays, slices, str, tuples.

  • Functions.
31 / 65

Arrays

  • Initialized one of two ways:

    let arr1 = [1, 2, 3]; // (array of 3 elements)
    let arr2 = [2; 30]; // (array of 30 values of `2`)
    println!("{}", arr1[1]); // ==> 2
  • arr1 is of type [i32; 3]; arr2 is of type [i32; 30]

  • Arrays types are written [T; N].
    • N is a compile-time constant. Arrays cannot be resized.
    • Array access is bounds-checked at runtime.
  • Arrays are indexed with [] (like most other languages).
32 / 65

Slices

  • Type &[T].
  • A "view" into an array by reference.
  • Not created directly, but are borrowed from other variables.
  • Can be mutable or immutable.
  • How do you know when a slice is still valid?
    • Coming soon... (ownership shenanigans!)
let arr = [0, 1, 2, 3, 4, 5];
let total_slice = &arr; // Slice all of `arr`
let total_slice = &arr[..]; // Same, but more explicit
let partial_slice = &arr[2..5]; // [2, 3, 4]
33 / 65

Strings

  • Two types of Rust strings: String and &str.
  • String is a heap-allocated, growable vector of characters.
  • &str is a type¹ that's used to slice into Strings.
  • String literals like "foo" are of type &str.
let s: &str = "galaxy";
let s2: String = "galaxy".to_string();
let s3: String = String::from("galaxy");
let s4: &str = &s3;

¹str is an unsized type, which doesn't have a compile-time known size, and therefore cannot exist by itself.

34 / 65

Tuples

  • Fixed-size, ordered, heterogeneous lists
  • Index into tuples with foo.0, foo.1, etc.
  • Can be destructured in let bindings, and used for variable bindings.
let foo: (i32, char, f64) = (72, 'H', 5.1);
let (x, y, z) = (72, 'H', 5.1);
let (a, b, c) = foo; // a = 72, b = 'H', c = 5.1
35 / 65

Function Objects

  • Two types:
    • Function pointers (a reference to a normal function)
    • Closures
  • Types are much more straightforward than C function pointers:
let x: fn(i32) -> i32 = square;
  • Can be passed by reference:
fn apply_twice(f: &Fn(i32) -> i32, x: i32) -> i32 {
f(f(x))
}
// ...
let y = apply_twice(&square, 5);
36 / 65

Casting

  • Cast between types with as:
let x: i32 = 100;
let y: u32 = x as u32;
  • Naturally, you can only cast between types that are safe to cast between.
    • No casting [i16; 4] to char! (This is called a "non-scalar" cast)
    • There are unsafe mechanisms to overcome this, if you know what you're doing.
37 / 65

Vec<T>

  • The standard library type
  • This is in Rust's "prelude": you don't need to import anything to use it.
  • A Vec (read "vector") is a heap-allocated growable array.
    • (cf. Java's ArrayList, C++'s std::vector, etc.)
  • <T> denotes a generic type.
    • The type of a Vec of i32s is Vec<i32>.
  • Create Vecs with Vec::new() or the vec! macro.
    • Namespacing: new is a function defined for the Vec struct.
38 / 65

Vec<T>

// Explicit typing
let v0: Vec<i32> = Vec::new();
// v1 and v2 are equal
let mut v1 = Vec::new();
v1.push(1);
v1.push(2);
v1.push(3);
let v2 = vec![1, 2, 3];
// v3 and v4 are equal
let v3 = vec![0; 4];
let v4 = vec![0, 0, 0, 0];
39 / 65

Vec<T>

let v2 = vec![1, 2, 3];
let x = v2[2]; // 3
  • Like arrays, vectors can be indexed with [].

    • Vectors must be indexed with usize values (guaranteed to be the same size as a pointer).
    • Other values can be cast to usize:
      let i: i8 = 2;
      let y = v2[i as usize];
  • Vectors has an extensive stdlib method list, which can be found at the offical Rust documentation.

40 / 65

References

  • Reference types are written with &: &i32.
  • References can be taken with & (like C/C++).
  • References can be dereferenced with * (like C/C++).
  • References are guaranteed to be valid.
    • Validity is enforced through compile-time checks!
  • These are not the same as (raw) pointers!
  • More ownership shenanigans next week.
let x = 12;
let ref_x = &x;
println!("{}", *ref_x); // 12
41 / 65

Control Flow

42 / 65

If Statements

if x > 0 {
10
} else if x == 0 {
0
} else {
println!("Not greater than zero!");
-10
}
  • No parens necessary.
  • Entire if statement is a single expression and will evaluate to a single value
    • Every arm must end with an expression of the same type.
    • That type must be () if you omit the else branch:
if x <= 0 {
println!("Too small!")
}
43 / 65

Loops

  • Loops come in three flavors: while, loop, and for.

    • break and continue exist just like in most languages
  • while works just like you'd expect:

let mut x = 0;
while x < 100 {
x += 1;
println!("x: {}", x);
}
44 / 65

Loops

  • loop is equivalent to while true.
    • Plus, the compiler can make optimizations knowing that it's infinite.
let mut x = 0;
loop {
x += 1;
println!("x: {}", x);
}
45 / 65

I actually always expect that Rust would... stop you from doing this. But it doesn't.

Loops

  • for is the most different from most C-like languages
    • for loops use an iterator expression:
    • n..m creates an iterator from n to m (exclusive).
    • n...m (inclusive range) is currently experimental!
// Loops from 0 to 9.
for x in 0..10 {
println!("{}", x);
}
46 / 65

We will definitely talk more about how these iterators work.

Loops

  • Some data structures can be used as iterators, like Vecs.
  • Only slices can be iterated over, not arrays.
let xs = [0, 1, 2, 3, 4];
for x in &xs {
println!("{}", x);
}
47 / 65

Match statements

let x = 3;
match x {
1 => println!("one fish"), // <- comma required
2 => {
println!("two fish");
println!("two fish");
}, // <- comma optional when using braces
_ => println!("no fish for you"), // "otherwise" case
}
  • match takes an expression (x) and branches on a list of value => expression statements.
  • The entire match evaluates to one expression.
    • Like if, all arms must evaluate to the same type.
  • _ is commonly used as a catch-all (cf. Haskell, OCaml).
48 / 65

Match statements

let x = 3;
let y = -3;
match (x, y) {
(1, 1) => println!("one"),
(2, j) => println!("two, {}", j),
(_, 3) => println!("three"),
(i, j) if i > 5 && j < 0 => println!("On guard!"),
(_, _) => println!(":<"),
}
  • The matched expression can be any expression (l-value), including tuples and function calls.
    • Matches can bind variables. _ is a throw-away variable name.
  • You must write an exhaustive match in order to compile.
  • Use if-guards to constrain a match to certain conditions.
  • Patterns can get very complex, as we'll see later.
49 / 65

Macros!

  • Macros are like functions, but they're named with ! at the end.
  • Can do generally very powerful stuff.
    • They actually generate code at compile time!
  • Call and use macros like functions.
  • You can define your own with macro_rules! macro_name blocks.
    • These are very complicated. More later!
  • Because they're so powerful, a lot of common utilities are defined as macros.
50 / 65

print! & println!

  • Print stuff out.
  • Use {} for general string interpolation, and {:?} for debug printing.
    • Some types can only be printed with {:?}, like arrays and Vecs.
print!("{}, {}, {}", "foo", 3, true);
// => foo, 3, true
println!("{:?}, {:?}", "foo", [1, 2, 3]);
// => "foo", [1, 2, 3]
51 / 65

format!

  • Uses println!-style string interpolation to create formatted Strings.
let fmted = format!("{}, {:x}, {:?}", 12, 155, Some("Hello"));
// fmted == "12, 9b, Some("Hello")"
52 / 65

panic!(msg)

  • Exits current task with given message.
  • Don't do this lightly! It is better to handle and report errors explicitly.
if x < 0 {
panic!("Oh noes!");
}
53 / 65

assert! & assert_eq!

  • assert!(condition) panics if condition is false.
  • assert_eq!(left, right) panics if left != right.
  • Useful for testing and catching illegal conditions.
#[test]
fn test_something() {
let actual = 1 + 2;
assert!(actual == 3);
assert_eq!(3, actual);
}
54 / 65

unreachable!()

  • Used to indicate that some code should not be reached.
  • panic!s when reached.
  • Can be useful to track down unexpected bugs (e.g. optimization bugs).
if false {
unreachable!();
}
55 / 65

unimplemented!()

  • Shorthand for panic!("not yet implemented")
fn sum(x: Vec<i32>) -> i32 {
// TODO
unimplemented!();
}
56 / 65

Rust Environment & Tools

57 / 65

Rustc

  • rustc program.rs compiles program.rs into an executable program.
    • Warnings are enabled by default.
    • Read all of the output! It may be verbose but it is very useful.
  • rustc doesn't need to be called once for each file like in C.
    • The build dependency tree is inferred from module declarations in the Rust code (starting at main.rs or lib.rs).
  • Typically, you'll instead use cargo, Rust's package manager and build tool.
58 / 65

Error output has gotten significantly better recently!

Cargo

  • Rust's package manager, build tool
  • Create a new project:
    • cargo new project_name (library)
    • cargo new project_name --bin (executable)
  • Build your project: cargo build
  • Run your tests: cargo test
59 / 65

Cargo is similar to Python virtualenv, Ruby's bundler

Cargo.toml

  • Cargo uses the Cargo.toml file to declare and manage dependencies and project metadata.
    • TOML is a simple format similar to INI.
  • More in your first homework assignments.
[package]
name = "Rust"
version = "0.1.0"
authors = ["Ferris <cis198@seas.upenn.edu>"]
[dependencies]
uuid = "0.1"
rand = "0.3"
[profile.release]
opt-level = 3
debug = false
60 / 65

cargo test

  • A test is any function annotated with #[test].
  • cargo test will run all annotated functions in your project.
  • Any function which executes without crashing (panic!ing) succeeds.
  • Use assert! (or assert_eq!) to check conditions (and panic! on failure)
  • You'll use this in HW01.
#[test]
fn it_works() {
// ...
}
61 / 65

cargo check

  • Not available by default!
  • Run cargo install cargo-check to install it.
  • Functionally the same as cargo build, but doesn't actually generate any code.
    • => Faster!
62 / 65

HW00: Hello Cargo & Hello Rust

  • Due Wednesday, 2016-09-07, 11:59pm.
  • Install rustup: manages installations of multiple versions of Rust.
    • Supports Linux / OS X / Windows.
    • Or use the 19x VM.
  • Submitting with GitHub Classroom is as easy as pie pushing to your private repo.
63 / 65

HW01: Finger Exercises

  • Due Wednesday, 2016-09-07, 11:59pm.
  • Introduction to Rust with "finger exercises". Use this lecture as a resource!
    • Sieve of Eratosthenes, Tower of Hanoi
64 / 65

Next Time

  • Ownership, references, borrowing
  • Structured data: structs, enums
  • Methods

Some code examples taken from The Rust Programming Language.

65 / 65

Lecture 00: Hello, Rust!

This lecture online:
GitHub One-Page ViewSlide View
2 / 65

Like I'll explain a little later, this class is run almost entirely through GitHub, so you can actually access the original/Markdown view of the slides -- say, if you wanted to search a lecture for something. Otherwise, the rendered view of the slides is on the website.

Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow