Introduce yourself, ask TAs to introduce if they're there.
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.
Introduce yourself, ask TAs to introduce if they're there.
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.
"Rust is a systems programming language that runs blazingly fast, prevents nearly all segfaults, and guarantees thread safety."
unsafe
code to get circumvent... most things.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 |
Office hours held in the Levine 6th floor lounge.
Hello, Rust!
fn main() { println!("Hello, CIS 198!");}
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 xlet mut y = 5;y += 1; // OK!
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 scopeprintln!("{}", x); // ==> 17
fn
.->
.var: type
.fn foo(x: u32) -> i32 { // ...}
foo(42);
More self-documenting and provides another layer of protection against bugs.
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;}
()
.()
.()
has only one value: ()
.()
is the default return type.()
.()
.fn foo() -> i32 { 5 }fn bar() -> () { () }fn baz() -> () { 5; }fn qux() { 5; }
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
printf
's "%s"
in C/C++./// 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! */ */}
Even if the parser for this syntax highlighter doesn't believe me.
bool
: true
and false
.char
: 'c'
or '😺'
(char
s 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)10i8
, 10u16
, 10.0f32
, 10usize
.i32
or f64
:10
defaults to i32
, 10.0
defaults to f64
.Arrays, slices, str
, tuples.
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]
[T; N]
.N
is a compile-time constant. Arrays cannot be resized.[]
(like most other languages).&[T]
.let arr = [0, 1, 2, 3, 4, 5];let total_slice = &arr; // Slice all of `arr`let total_slice = &arr[..]; // Same, but more explicitlet partial_slice = &arr[2..5]; // [2, 3, 4]
String
and &str
.String
is a heap-allocated, growable vector of characters.&str
is a type¹ that's used to slice into String
s."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.
foo.0
, foo.1
, etc.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
let x: fn(i32) -> i32 = square;
fn apply_twice(f: &Fn(i32) -> i32, x: i32) -> i32 { f(f(x))}// ...let y = apply_twice(&square, 5);
as
:let x: i32 = 100;let y: u32 = x as u32;
[i16; 4]
to char
! (This is called a "non-scalar" cast)Vec<T>
Vec
(read "vector") is a heap-allocated growable array.ArrayList
, C++'s std::vector
, etc.)<T>
denotes a generic type.Vec
of i32
s is Vec<i32>
.Vec
s with Vec::new()
or the vec!
macro.new
is a function defined for the Vec
struct.Vec<T>
// Explicit typinglet v0: Vec<i32> = Vec::new();// v1 and v2 are equallet mut v1 = Vec::new();v1.push(1);v1.push(2);v1.push(3);let v2 = vec![1, 2, 3];
// v3 and v4 are equallet v3 = vec![0; 4];let v4 = vec![0, 0, 0, 0];
Vec<T>
let v2 = vec![1, 2, 3];let x = v2[2]; // 3
Like arrays, vectors can be indexed with []
.
usize
values (guaranteed to be the same size as a pointer).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.
&
: &i32
.&
(like C/C++).*
(like C/C++).let x = 12;let ref_x = &x;println!("{}", *ref_x); // 12
if x > 0 { 10} else if x == 0 { 0} else { println!("Not greater than zero!"); -10}
()
if you omit the else
branch:if x <= 0 { println!("Too small!")}
Loops come in three flavors: while
, loop
, and for
.
break
and continue
exist just like in most languageswhile
works just like you'd expect:
let mut x = 0;while x < 100 { x += 1; println!("x: {}", x);}
loop
is equivalent to while true
.let mut x = 0;loop { x += 1; println!("x: {}", x);}
I actually always expect that Rust would... stop you from doing this. But it doesn't.
for
is the most different from most C-like languagesfor
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);}
We will definitely talk more about how these iterators work.
Vec
s.let xs = [0, 1, 2, 3, 4];for x in &xs { println!("{}", x);}
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.if
, all arms must evaluate to the same type._
is commonly used as a catch-all (cf. Haskell, OCaml).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!(":<"),}
_
is a throw-away variable name.if
-guards to constrain a match to certain conditions.!
at the end.macro_rules! macro_name
blocks.print!
& println!
{}
for general string interpolation, and {:?}
for debug printing.{:?}
, like arrays and Vec
s.print!("{}, {}, {}", "foo", 3, true);// => foo, 3, trueprintln!("{:?}, {:?}", "foo", [1, 2, 3]);// => "foo", [1, 2, 3]
format!
println!
-style string interpolation to create formatted String
s.let fmted = format!("{}, {:x}, {:?}", 12, 155, Some("Hello"));// fmted == "12, 9b, Some("Hello")"
panic!(msg)
if x < 0 { panic!("Oh noes!");}
assert!
& assert_eq!
assert!(condition)
panics if condition
is false
.assert_eq!(left, right)
panics if left != right
.#[test]fn test_something() { let actual = 1 + 2; assert!(actual == 3); assert_eq!(3, actual);}
unreachable!()
panic!
s when reached.if false { unreachable!();}
unimplemented!()
panic!("not yet implemented")
fn sum(x: Vec<i32>) -> i32 { // TODO unimplemented!();}
rustc program.rs
compiles program.rs
into an executable program
.rustc
doesn't need to be called once for each file like in C.main.rs
or lib.rs
).cargo
, Rust's package manager and build tool.Error output has gotten significantly better recently!
cargo new project_name
(library)cargo new project_name --bin
(executable)cargo build
cargo test
Cargo is similar to Python virtualenv, Ruby's bundler
Cargo.toml
file to declare and manage dependencies and
project metadata.[package]name = "Rust"version = "0.1.0"authors = ["Ferris <cis198@seas.upenn.edu>"][dependencies]uuid = "0.1"rand = "0.3"[profile.release]opt-level = 3debug = false
cargo test
#[test]
.cargo test
will run all annotated functions in your project.panic!
ing) succeeds.assert!
(or assert_eq!
) to check conditions (and panic!
on failure)#[test]fn it_works() { // ...}
cargo check
cargo install cargo-check
to install it.cargo build
, but doesn't actually generate any code.rustup
: manages installations of multiple versions of Rust.Some code examples taken from The Rust Programming Language.
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.
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 |