├── 10 ├── content.md └── index.html ├── 11 ├── content.md └── index.html ├── 12 ├── content.md └── index.html ├── 13 ├── content.md └── index.html ├── 14 ├── content.md └── index.html ├── 15 ├── content.md └── index.html ├── 16 ├── content.md └── index.html ├── 17 ├── content.md └── index.html ├── 18 ├── content.md └── index.html ├── .gitignore ├── 00 ├── content.md ├── img │ ├── cobol.png │ ├── ferris.png │ ├── next_time.jpg │ ├── rust-in-production.png │ └── rust.png └── index.html ├── 01 ├── content.md ├── img │ └── holy-grail.jpg └── index.html ├── 02 ├── content.md └── index.html ├── 03 ├── content.md └── index.html ├── 04 ├── content.md └── index.html ├── 05 ├── content.md ├── img │ ├── collector.jpg │ └── consume.png └── index.html ├── 06 ├── content.md └── index.html ├── 07 ├── content.md └── index.html ├── 08 ├── content.md └── index.html ├── 09 ├── content.md ├── img │ ├── web1.png │ └── web2.png └── index.html ├── Gemfile ├── Gemfile.lock ├── README.md ├── _config.yml ├── _includes └── head.html ├── _layouts └── slides.html ├── css ├── common.css └── slides.css ├── favicon.ico ├── index.html └── js ├── playpen.198.js └── remark.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/jekyll,vim,linux 3 | 4 | ### Jekyll ### 5 | _site/ 6 | .sass-cache/ 7 | .jekyll-metadata 8 | 9 | 10 | ### Vim ### 11 | [._]*.s[a-w][a-z] 12 | [._]s[a-w][a-z] 13 | *.un~ 14 | Session.vim 15 | .netrwhist 16 | *~ 17 | 18 | 19 | ### Linux ### 20 | *~ 21 | 22 | # KDE directory preferences 23 | .directory 24 | 25 | # Linux trash folder which might appear on any partition or disk 26 | .Trash-* 27 | 28 | -------------------------------------------------------------------------------- /00/img/cobol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/00/img/cobol.png -------------------------------------------------------------------------------- /00/img/ferris.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/00/img/ferris.png -------------------------------------------------------------------------------- /00/img/next_time.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/00/img/next_time.jpg -------------------------------------------------------------------------------- /00/img/rust-in-production.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/00/img/rust-in-production.png -------------------------------------------------------------------------------- /00/img/rust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/00/img/rust.png -------------------------------------------------------------------------------- /00/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "00 - Course Introduction" 4 | --- 5 | -------------------------------------------------------------------------------- /01/content.md: -------------------------------------------------------------------------------- 1 | # Ownership & Lifetimes 2 | 3 | ### CIS 198 Lecture 1 4 | 5 | --- 6 | ## Ownership & Borrowing 7 | 8 | - Explicit ownership is the biggest new feature that Rust brings to the table! 9 | - Ownership is all¹ checked at compile time! 10 | - Newcomers to Rust often find themselves "fighting with the borrow checker" 11 | trying to get their code to compile 12 | 13 | ¹*mostly* 14 | 15 | ??? 16 | 17 | - The ownership model is the biggest thing that Rust brings to the table, its 18 | claim to fame. 19 | - Ownership is something that's checked at compile time and has as little 20 | runtime cost as possible. 21 | - So it's zero (or very little) runtime cost, but you pay for it with a longer 22 | compilation time and learning curve. Which is where the phrase "fighitng with 23 | the borrow checker" comes from, when you have to work around the compiler's 24 | restrictions to figure out how to do what you want. 25 | 26 | --- 27 | ## Ownership 28 | 29 | - A variable binding _takes ownership_ of its data. 30 | - A piece of data can only have one owner at a time. 31 | - When a binding goes out of scope, the bound data is released automatically. 32 | - For heap-allocated data, this means de-allocation. 33 | - Data _must be guaranteed_ to outlive its references. 34 | 35 | ```rust 36 | fn foo() { 37 | // Creates a Vec object. 38 | // Gives ownership of the Vec object to v1. 39 | let mut v1 = vec![1, 2, 3]; 40 | 41 | v1.pop(); 42 | v1.push(4); 43 | 44 | // At the end of the scope, v1 goes out of scope. 45 | // v1 still owns the Vec object, so it can be cleaned up. 46 | } 47 | ``` 48 | 49 | ??? 50 | 51 | So here are the basics. 52 | - When you introduce a variable binding, it takes ownership of its data. And a 53 | piece of data can only have one owner at a time. 54 | - When a variable binding goes out of scope, nothing has access to the data 55 | anymore, so it can be released. Which means, if it's on the heap, it can be 56 | de-allocated. 57 | - And data must be guaranteed to outlive its references. Or, all references are 58 | guaranteed to be valid. 59 | 60 | --- 61 | ## Move Semantics 62 | 63 | ```rust 64 | let v1 = vec![1, 2, 3]; 65 | 66 | // Ownership of the Vec object moves to v2. 67 | let v2 = v1; 68 | 69 | println!("{}", v1[2]); // error: use of moved value `v1` 70 | ``` 71 | 72 | - `let v2 = v1;` 73 | - We don't want to copy the data, since that's expensive. 74 | - The data cannot have multiple owners. 75 | - Solution: move the Vec's ownership into `v2`, and declare `v1` invalid. 76 | - `println!("{}", v1[2]);` 77 | - We know that `v1` is no longer a valid variable binding, so this is an error. 78 | - Rust can reason about this at compile time, so it throws a compiler error. 79 | 80 | ??? 81 | 82 | Here's another example: 83 | - Line 1: declare a vector v1. 84 | - Line 2: let v2 = v1. Ownership of the vector is moved from v1 to v2. 85 | - we don't want to move or copy the data, that's expensive and causes other 86 | bugs 87 | - we already know the data can't have multiple owners 88 | - Line 3: try to print v1. 89 | - but since the vector has been moved out of v1, it is no longer a valid 90 | variable binding 91 | - all of this happens at compile time. 92 | 93 | --- 94 | ## Move Semantics 95 | 96 | - Moving ownership is a compile-time semantic; it doesn't involve moving data 97 | during your program. 98 | - Moves are automatic (via assignments); no need to use something like C++'s 99 | `std::move`. 100 | - However, there are functions like `std::mem::replace` in Rust to provide 101 | advanced ownership management. 102 | 103 | ??? 104 | 105 | - Moving ownership is an impliict operation done at compile time. No data is 106 | moved or copied around when your program is being run. 107 | - The movement of data is automatic, you don't need to call anything like 108 | std::move (as in C++). 109 | - But you can do more fine-grained ownership or memory movement with a number of 110 | standrard library functions, like std::mem::replace. 111 | 112 | --- 113 | ## Ownership 114 | 115 | - Ownership does not always have to be moved. 116 | - What would happen if it did? Rust would get very tedious to write: 117 | 118 | ```rust 119 | fn vector_length(v: Vec) -> Vec { 120 | // Do whatever here, 121 | // then return ownership of `v` back to the caller 122 | } 123 | ``` 124 | - You could imagine that this does not scale well either. 125 | - The more variables you had to hand back, the longer your return type would be! 126 | - Imagine having to pass ownership around for 5+ variables at a time :( 127 | 128 | ??? 129 | 130 | - Ownership doesn't have to be moved. 131 | - If it did, you would also have to return ownership at the end of every 132 | function, or have all of your variables constantly going out of scope. 133 | - This gets absurd very quickly, imagine having to return all of your function 134 | arguments as return values just to make sure they don't go out of scope. 135 | 136 | --- 137 | ## Borrowing 138 | 139 | - Obviously, this is not the case. 140 | - Instead of transferring ownership, we can _borrow_ data. 141 | - A variable's data can be borrowed by taking a reference to the variable; 142 | ownership doesn't change. 143 | - When a reference goes out of scope, the borrow is over. 144 | - The original variable retains ownership throughout. 145 | 146 | ```rust 147 | let v = vec![1, 2, 3]; 148 | 149 | // v_ref is a reference to v. 150 | let v_ref = &v; 151 | 152 | // use v_ref to access the data in the vector v. 153 | assert_eq!(v[1], v_ref[1]); 154 | ``` 155 | 156 | ??? 157 | 158 | - Obviously, this is not the case in Rust, otherwise the language would be 159 | impossible to use. 160 | - Instead, we can temporarily transfer ownership by borrowing data. 161 | - The way that borrowing works is: you can take a reference to the original 162 | variable and use it to access the data. 163 | - When a reference goes out of scope, the borrow is over. 164 | - However, the original variable retains ownership during the borrow and 165 | afterwards. 166 | 167 | --- 168 | ## Borrowing 169 | 170 | - Caveat: this adds restrictions to the original variable. 171 | - Ownership cannot be transferred from a variable while references to it exist. 172 | - That would invalidate the reference. 173 | 174 | ```rust 175 | let v = vec![1, 2, 3]; 176 | 177 | // v_ref is a reference to v. 178 | let v_ref = &v; 179 | 180 | // Moving ownership to v_new would invalidate v_ref. 181 | // error: cannot move out of `v` because it is borrowed 182 | let v_new = v; 183 | ``` 184 | 185 | ??? 186 | 187 | - This adds a caveat: ownership cannot be ransferred *from* a variable that is 188 | currently being borrowed, because that would invalidate the reference. 189 | 190 | --- 191 | ## Borrowing 192 | 193 | ```rust 194 | /// `length` only needs `vector` temporarily, so it is borrowed. 195 | fn length(vec_ref: &Vec) -> usize { 196 | // vec_ref is auto-dereferenced when you call methods on it. 197 | vec_ref.len() 198 | // you can also explicitly dereference. 199 | // (*vec_ref).len() 200 | } 201 | 202 | fn main() { 203 | let vector = vec![]; 204 | length(&vector); 205 | println!("{:?}", vector); // this is fine 206 | } 207 | ``` 208 | - Note the type of `length`: `vec_ref` is passed by reference, so it's now an `&Vec`. 209 | - References, like bindings, are *immutable* by default. 210 | - The borrow is over after the reference goes out of scope (at the end of `length`). 211 | 212 | --- 213 | ## Borrowing 214 | 215 | ```rust 216 | /// `push` needs to modify `vector` so it is borrowed mutably. 217 | fn push(vec_ref: &mut Vec, x: i32) { 218 | vec_ref.push(x); 219 | } 220 | 221 | fn main() { 222 | let mut vector: Vec = vec![]; 223 | let vector_ref: &mut Vec = &mut vector; 224 | push(vector_ref, 4); 225 | } 226 | ``` 227 | - Variables can be borrowed by _mutable_ reference: `&mut vec_ref`. 228 | - `vec_ref` is a reference to a mutable `Vec`. 229 | - The type is `&mut Vec`, not `&Vec`. 230 | - Different from a reference which is variable. 231 | 232 | --- 233 | ## Borrowing 234 | 235 | ```rust 236 | /// `push` needs to modify `vector` so it is borrowed mutably. 237 | fn push2(vec_ref: &mut Vec, x: i32) { 238 | // error: cannot move out of borrowed content. 239 | let vector = *vec_ref; 240 | vector.push(x); 241 | } 242 | 243 | fn main() { 244 | let mut vector = vec![]; 245 | push2(&mut vector, 4); 246 | } 247 | ``` 248 | - Error! You can't dereference `vec_ref` into a variable binding because that 249 | would change the ownership of the data. 250 | 251 | --- 252 | ## Borrowing 253 | 254 | - Rust will auto-dereference variables... 255 | - When making method calls on a reference. 256 | - When passing a reference as a function argument. 257 | 258 | ```rust 259 | /// `length` only needs `vector` temporarily, so it is borrowed. 260 | fn length(vec_ref: &&Vec) -> usize { 261 | // vec_ref is auto-dereferenced when you call methods on it. 262 | vec_ref.len() 263 | } 264 | 265 | fn main() { 266 | let vector = vec![]; 267 | length(&&&&&&&&&&&&vector); 268 | } 269 | ``` 270 | 271 | --- 272 | ## Borrowing 273 | 274 | - You will have to dereference variables... 275 | - When writing into them. 276 | - And other times that usage may be ambiguous. 277 | 278 | ```rust 279 | let mut a = 5; 280 | let ref_a = &mut a; 281 | *ref_a = 4; 282 | println!("{}", *ref_a + 4); 283 | // ==> 8 284 | ``` 285 | 286 | --- 287 | ## `ref` 288 | 289 | ```rust 290 | let mut vector = vec![0]; 291 | 292 | { 293 | // These are equivalent 294 | let ref1 = &vector; 295 | let ref ref2 = vector; 296 | assert_eq!(ref1, ref2); 297 | } 298 | 299 | let ref mut ref3 = vector; 300 | ref3.push(1); 301 | ``` 302 | 303 | - When binding a variable, `ref` can be applied to make the variable a reference to the assigned value. 304 | - Take a mutable reference with `ref mut`. 305 | - This is most useful in `match` statements when destructuring patterns. 306 | 307 | --- 308 | ## `ref` 309 | 310 | ```rust 311 | let mut vectors = (vec![0], vec![1]); 312 | match vectors { 313 | (ref v1, ref mut v2) => { 314 | v1.len(); 315 | v2.push(2); 316 | } 317 | } 318 | ``` 319 | - Use `ref` and `ref mut` when binding variables inside match statements. 320 | 321 | --- 322 | ## `Copy` Types 323 | 324 | - Rust defines a trait¹ named `Copy` that signifies that a type may be 325 | copied instead whenever it would be moved. 326 | - Most primitive types are `Copy` (`i32`, `f64`, `char`, `bool`, etc.) 327 | - Types that contain references may not be `Copy` (e.g. `Vec`, `String`). 328 | ```rust 329 | let x: i32 = 12; 330 | let y = x; // `i32` is `Copy`, so it's not moved :D 331 | println!("x still works: {}, and so does y: {}", x, y); 332 | ``` 333 | 334 | ¹ Like a Java interface or Haskell typeclass 335 | 336 | ??? 337 | 338 | This is why we've been using Vectors as examples in this slide set. 339 | 340 | --- 341 | ## Borrowing Rules 342 | ##### _The Holy Grail of Rust_ 343 | Learn these rules, and they will serve you well. 344 | 345 | - You can't keep borrowing something after it stops existing. 346 | - One object may have many immutable references to it (`&T`). 347 | - **OR** _exactly one_ mutable reference (`&mut T`) (not both). 348 | - That's it! 349 | 350 | ![](img/holy-grail.jpg) 351 | 352 | --- 353 | ### Borrowing Prevents... 354 | 355 | - Iterator invalidation due to mutating a collection you're iterating over. 356 | - This pattern can be written in C, C++, Java, Python, Javascript... 357 | - But may result in, e.g, `ConcurrentModificationException` (at runtime!) 358 | 359 | ```rust 360 | let mut vs = vec![1,2,3,4]; 361 | for v in &vs { 362 | vs.pop(); 363 | // ERROR: cannot borrow `vs` as mutable because 364 | // it is also borrowed as immutable 365 | } 366 | ``` 367 | 368 | - `pop` needs to borrow `vs` as mutable in order to modify the data. 369 | - But `vs` is being borrowed as immutable by the loop! 370 | 371 | --- 372 | ### Borrowing Prevents... 373 | 374 | - Use-after-free 375 | - Valid in C, C++... 376 | 377 | ```rust 378 | let y: &i32; 379 | { 380 | let x = 5; 381 | y = &x; // error: `x` does not live long enough 382 | } 383 | println!("{}", *y); 384 | ``` 385 | 386 | - The full error message: 387 | 388 | ``` 389 | error: `x` does not live long enough 390 | note: reference must be valid for the block suffix following statement 391 | 0 at 1:16 392 | ...but borrowed value is only valid for the block suffix 393 | following statement 0 at 4:18 394 | ``` 395 | 396 | - This eliminates a _huge_ number of memory safety bugs _at compile time_. 397 | 398 | ??? 399 | 400 | As a side note, this technique of creating a block to limit the scope of a 401 | variable (in this case x) is pretty useful. 402 | 403 | --- 404 | ## Example: Vectors 405 | 406 | - You can iterate over `Vec`s in three different ways: 407 | 408 | ```rust 409 | let mut vs = vec![0,1,2,3,4,5,6]; 410 | 411 | // Borrow immutably 412 | for v in &vs { // Can also write `for v in vs.iter()` 413 | println!("I'm borrowing {}.", v); 414 | } 415 | 416 | // Borrow mutably 417 | for v in &mut vs { // Can also write `for v in vs.iter_mut()` 418 | *v = *v + 1; 419 | println!("I'm mutably borrowing {}.", v); 420 | } 421 | 422 | // Take ownership of the whole vector 423 | for v in vs { // Can also write `for v in vs.into_iter()` 424 | println!("I now own {}! AHAHAHAHA!", v); 425 | } 426 | 427 | // `vs` is no longer valid 428 | ``` 429 | -------------------------------------------------------------------------------- /01/img/holy-grail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/01/img/holy-grail.jpg -------------------------------------------------------------------------------- /01/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "01 - Ownership & Lifetimes" 4 | --- 5 | -------------------------------------------------------------------------------- /02/content.md: -------------------------------------------------------------------------------- 1 | # Structured Data 2 | 3 | ### CIS 198 Lecture 2 4 | 5 | --- 6 | ## Structured Data 7 | 8 | - Rust has two simple ways of creating structured data types: 9 | - Structs: C-like structs to hold data. 10 | - Enums: OCaml-like; data that can be one of several types. 11 | 12 | - Structs and enums may have one or more implementation blocks (`impl`s) which 13 | define methods for the data type. 14 | 15 | --- 16 | ## Structs 17 | 18 | - A struct declaration: 19 | - Fields are declared with `name: type`. 20 | 21 | ```rust 22 | struct Point { 23 | x: i32, 24 | y: i32, 25 | } 26 | ``` 27 | 28 | - By convention, structs have `CamelCase` names, and their fields have `snake_case` names. 29 | - Structs may be instantiated with fields assigned in braces. 30 | 31 | ```rust 32 | let origin = Point { x: 0, y: 0 }; 33 | ``` 34 | 35 | --- 36 | ## Structs 37 | 38 | - Struct fields may be accessed with dot notation. 39 | - Structs may not be partially-initialized. 40 | - You must assign all fields upon creation, or declare an uninitialized 41 | struct that you initialize later. 42 | 43 | ```rust 44 | let mut p = Point { x: 19, y: 8 }; 45 | p.x += 1; 46 | p.y -= 1; 47 | ``` 48 | 49 | --- 50 | ## Structs 51 | 52 | - Structs do not have field-level mutability. 53 | - Mutability is a property of the **variable binding**, not the type. 54 | - Field-level mutability (interior mutability) can be achieved via `Cell` types. 55 | - More on these very soon. 56 | 57 | ```rust 58 | struct Point { 59 | x: i32, 60 | mut y: i32, // Illegal! 61 | } 62 | ``` 63 | 64 | --- 65 | ## Structs 66 | 67 | - Structs are namespaced with their module name. 68 | - The fully qualified name of `Point` is `foo::Point`. 69 | - Struct fields are private by default. 70 | - They may be made public with the `pub` keyword. 71 | - Private fields may only be accessed from within the module where the struct is 72 | declared. 73 | 74 | ```rust 75 | mod foo { 76 | pub struct Point { 77 | pub x: i32, 78 | y: i32, 79 | } 80 | } 81 | 82 | fn main() { 83 | let b = foo::Point { x: 12, y: 12 }; 84 | // ^~~~~~~~~~~~~~~~~~~~~~~~~~~ 85 | // error: field `y` of struct `foo::Point` is private 86 | } 87 | ``` 88 | 89 | --- 90 | ## Structs 91 | 92 | ```rust 93 | mod foo { 94 | pub struct Point { 95 | pub x: i32, 96 | y: i32, 97 | } 98 | 99 | // Creates and returns a new point 100 | pub fn new(x: i32, y: i32) -> Point { 101 | Point { x: x, y: y } 102 | } 103 | } 104 | ``` 105 | 106 | - `new` is inside the same module as `Point`, so accessing private fields is 107 | allowed. 108 | 109 | --- 110 | ### Struct `match`ing 111 | 112 | - Destructure structs with `match` statements. 113 | 114 | ```rust 115 | pub struct Point { 116 | x: i32, 117 | y: i32, 118 | } 119 | 120 | match p { 121 | Point { x, y } => println!("({}, {})", x, y) 122 | } 123 | ``` 124 | 125 | --- 126 | ### Struct `match`ing 127 | 128 | - Some other tricks for struct `match`es: 129 | 130 | ```rust 131 | match p { 132 | Point { y: y1, x: x1 } => println!("({}, {})", x1, y1) 133 | } 134 | 135 | match p { 136 | Point { y, .. } => println!("{}", y) 137 | } 138 | ``` 139 | - Fields do not need to be in order. 140 | - List fields inside braces to bind struct members to those variable names. 141 | - Use `struct_field: new_var_binding` to change the variable it's bound to. 142 | - Omit fields: use `..` to ignore all unnamed fields. 143 | 144 | --- 145 | ### Struct Update Syntax 146 | 147 | - A struct initializer can contain `.. s` to copy some or all fields from `s`. 148 | - Any fields you don't specify in the initializer get copied over from the target struct. 149 | - The struct used must be of the same type as the target struct. 150 | - No copying same-type fields from different-type structs! 151 | 152 | ```rust 153 | struct Foo { a: i32, b: i32, c: i32, d: i32, e: i32 } 154 | 155 | let mut x = Foo { a: 1, b: 1, c: 2, d: 2, e: 3 }; 156 | let x2 = Foo { e: 4, .. x }; 157 | 158 | // Useful to update multiple fields of the same struct: 159 | x = Foo { a: 2, b: 2, e: 2, .. x }; 160 | ``` 161 | 162 | --- 163 | ### Tuple Structs 164 | 165 | - Variant on structs that has a name, but no named fields. 166 | - Have numbered field accessors, like tuples (e.g. `x.0`, `x.1`, etc). 167 | - Can also `match` these. 168 | 169 | ```rust 170 | struct Color(i32, i32, i32); 171 | 172 | let mut c = Color(0, 255, 255); 173 | c.0 = 255; 174 | match c { 175 | Color(r, g, b) => println!("({}, {}, {})", r, g, b) 176 | } 177 | ``` 178 | 179 | --- 180 | ### Tuple Structs 181 | 182 | - Helpful if you want to create a new type that's not just an alias. 183 | - This is often referred to as the "newtype" pattern. 184 | - These two types are structurally identical, but not equatable. 185 | 186 | ```rust 187 | // Not equatable 188 | struct Meters(i32); 189 | struct Yards(i32); 190 | 191 | // May be compared using `==`, added with `+`, etc. 192 | type MetersAlias = i32; 193 | type YardsAlias = i32; 194 | ``` 195 | 196 | --- 197 | ### Unit Structs (Zero-Sized Types) 198 | 199 | - Structs can be declared to have zero size. 200 | - This struct has no fields! 201 | - We can still instantiate it. 202 | - It can be used as a "marker" type on other data structures. 203 | - Useful to indicate, e.g., the type of data a container is storing. 204 | 205 | ```rust 206 | struct Unit; 207 | 208 | let u = Unit; 209 | ``` 210 | 211 | --- 212 | ## Enums 213 | 214 | - An enum, or "sum type", is a way to express some data that may be one of several things. 215 | - Much more powerful than in Java, C, C++, C#... 216 | - Each enum variant can have: 217 | - no data (unit variant) 218 | - named data (struct variant) 219 | - unnamed ordered data (tuple variant) 220 | 221 | ```rust 222 | enum Resultish { 223 | Ok, 224 | Warning { code: i32, message: String }, 225 | Err(String) 226 | } 227 | ``` 228 | 229 | --- 230 | ## Enums 231 | 232 | - Enum variants are namespaced by their enum type: `Resultish::Ok`. 233 | - You can import all variants with `use Resultish::*`. 234 | - Enums, much as you'd expect, can be matched on like any other data type. 235 | 236 | ```rust 237 | match make_request() { 238 | Resultish::Ok => 239 | println!("Success!"), 240 | Resultish::Warning { code, message } => 241 | println!("Warning: {}!", message), 242 | Resultish::Err(s) => 243 | println!("Failed with error: {}", s), 244 | } 245 | ``` 246 | 247 | --- 248 | ## Enums 249 | 250 | - Enum constructors like `Resultish::Ok` and the like can be used as functions. 251 | - This is not currently very useful, but will become so when we cover closures & 252 | iterators. 253 | 254 | --- 255 | ## Recursive Types 256 | 257 | - You might think to create a nice functional-style `List` type: 258 | 259 | ```rust 260 | enum List { 261 | Nil, 262 | Cons(i32, List), 263 | } 264 | ``` 265 | 266 | --- 267 | ## Recursive Types 268 | 269 | - Such a definition would have infinite size at compile time! 270 | - Structs & enums are stored inline by default, so they may not be recursive. 271 | - i.e. elements are not stored by reference, unless explicitly specified. 272 | - The compiler tells us how to fix this, but what's a `box`? 273 | 274 | ```rust 275 | enum List { 276 | Nil, 277 | Cons(i32, List), 278 | } 279 | // error: invalid recursive enum type 280 | // help: wrap the inner value in a box to make it representable 281 | ``` 282 | 283 | --- 284 | ## Boxes, Briefly 285 | 286 | - A `box` (lowercase) is a general term for one of Rust's ways of allocating data on the heap. 287 | - A `Box` (uppercase) is a heap pointer with exactly one owner. 288 | - A `Box` owns its data (the `T`) uniquely-- it can't be aliased. 289 | - `Box`es are automatically destructed when they go out of scope. 290 | - Create a `Box` with `Box::new()`: 291 | 292 | ```rust 293 | let boxed_five = Box::new(5); 294 | 295 | enum List { 296 | Nil, 297 | Cons(i32, Box), // OK! 298 | } 299 | ``` 300 | - We'll cover these in greater detail when we talk more about pointers. 301 | 302 | --- 303 | ## Methods 304 | 305 | ```rust 306 | impl Point { 307 | pub fn distance(&self, other: Point) -> f32 { 308 | let (dx, dy) = (self.x - other.x, self.y - other.y); 309 | ((dx.pow(2) + dy.pow(2)) as f32).sqrt() 310 | } 311 | } 312 | 313 | fn main() { 314 | let p = Point { x: 1, y: 2 }; 315 | p.distance(); 316 | } 317 | ``` 318 | 319 | - Methods can be implemented for structs and enums in an `impl` block. 320 | - Like fields, methods may be accessed via dot notation. 321 | - Can be made public with `pub`. 322 | - `impl` blocks themselves don't need to be made `pub`. 323 | - Work for enums in exactly the same way they do for structs. 324 | 325 | --- 326 | ## Methods 327 | 328 | - The first argument to a method, named `self`, determines what kind of ownership the method 329 | requires. 330 | - `&self`: the method *borrows* the value. 331 | - Use this unless you need a different ownership model. 332 | - `&mut self`: the method *mutably borrows* the value. 333 | - The function needs to modify the struct it's called on. 334 | - `self`: the method takes ownership. 335 | - The function consumes the value and may return something else. 336 | 337 | --- 338 | ## Methods 339 | 340 | ```rust 341 | impl Point { 342 | fn distance(&self, other: Point) -> f32 { 343 | let (dx, dy) = (self.x - other.x, self.y - other.y); 344 | ((dx.pow(2) + dy.pow(2)) as f32).sqrt() 345 | } 346 | 347 | fn translate(&mut self, x: i32, y: i32) { 348 | self.x += x; 349 | self.y += y; 350 | } 351 | 352 | fn mirror_y(self) -> Point { 353 | Point { x: -self.x, y: self.y } 354 | } 355 | } 356 | ``` 357 | 358 | - `distance` needs to access but not modify fields. 359 | - `translate` modifies the struct fields. 360 | - `mirror_y` returns an entirely new struct, consuming the old one. 361 | 362 | --- 363 | ## Associated Functions 364 | 365 | ```rust 366 | impl Point { 367 | fn new(x: i32, y: i32) -> Point { 368 | Point { x: x, y: y } 369 | } 370 | } 371 | 372 | fn main() { 373 | let p = Point::new(1, 2); 374 | } 375 | ``` 376 | 377 | - Associated function: like a method, but does not take `self`. 378 | - This is called with namespacing syntax: `Point::new()`. 379 | - Not `Point.new()`. 380 | - Like a "static" method in Java. 381 | - A constructor-like function is usually named `new`. 382 | - No inherent notion of constructors, no automatic construction. 383 | 384 | --- 385 | ## Implementations 386 | - Methods, associated functions, and functions in general may not be overloaded. 387 | - e.g. `Vec::new()` and `Vec::with_capacity(capacity: usize)` are both 388 | constructors for `Vec` 389 | - Methods may not be inherited. 390 | - Rust structs & enums must be composed instead. 391 | - However, traits (coming soon) have basic inheritance. 392 | 393 | --- 394 | ## Patterns 395 | 396 | - Use `...` to specify a range of values. Useful for numerics and `char`s. 397 | - Use `_` to bind against any value (like any variable binding) and discard the 398 | binding. 399 | 400 | ```rust 401 | let x = 17; 402 | 403 | match x { 404 | 0 ... 5 => println!("zero through five (inclusive)"), 405 | _ => println!("You still lose the game."), 406 | } 407 | ``` 408 | 409 | --- 410 | ### `match`: References 411 | 412 | - Get a reference to a variable by asking for it with `ref`. 413 | 414 | ```rust 415 | let x = 17; 416 | 417 | match x { 418 | ref r => println!("Of type &i32: {}", r), 419 | } 420 | ``` 421 | 422 | - And get a mutable reference with `ref mut`. 423 | - Only if the variable was declared `mut`. 424 | 425 | ```rust 426 | let mut x = 17; 427 | 428 | match x { 429 | ref r if x == 5 => println!("{}", r), 430 | ref mut r => *r = 5 431 | } 432 | ``` 433 | - Similar to `let ref`. 434 | 435 | --- 436 | ### `if-let` Statements 437 | 438 | - If you only need a single match arm, it often makes more sense to use Rust's `if-let` construct. 439 | - For example, given the `Resultish` type we defined earlier: 440 | 441 | ```rust 442 | enum Resultish { 443 | Ok, 444 | Warning { code: i32, message: String }, 445 | Err(String), 446 | } 447 | ``` 448 | 449 | --- 450 | ### `if-let` Statements 451 | - Suppose we want to report an error but do nothing on `Warning`s and `Ok`s. 452 | 453 | ```rust 454 | match make_request() { 455 | Resultish::Err(_) => println!("Total and utter failure."), 456 | _ => println!("ok."), 457 | } 458 | ``` 459 | 460 | - We can simplify this statement with an `if-let` binding: 461 | 462 | ```rust 463 | let result = make_request(); 464 | 465 | if let Resultish::Err(s) = result { 466 | println!("Total and utter failure: {}", s); 467 | } else { 468 | println!("ok."); 469 | } 470 | ``` 471 | 472 | --- 473 | ### `while-let` Statement 474 | 475 | - There's also a similar `while-let` statement, which works like an `if-let`, 476 | but iterates until the condition fails to match. 477 | 478 | ```rust 479 | while let Resultish::Err(s) = make_request() { 480 | println!("Total and utter failure: {}", s); 481 | } 482 | ``` 483 | 484 | --- 485 | ### Inner Bindings 486 | 487 | - With more complicated data structures, use `@` to create variable bindings for 488 | inner elements. 489 | 490 | ```rust 491 | #[derive(Debug)] 492 | enum A { None, Some(B) } 493 | #[derive(Debug)] 494 | enum B { None, Some(i32) } 495 | 496 | fn foo(x: A) { 497 | match x { 498 | a @ A::None => println!("a is A::{:?}", a), 499 | ref a @ A::Some(B::None) => println!("a is A::{:?}", *a), 500 | A::Some(b @ B::Some(_)) => println!("b is B::{:?}", b), 501 | } 502 | } 503 | 504 | foo(A::None); // ==> x is A::None 505 | foo(A::Some(B::None)); // ==> a is A::Some(None) 506 | foo(A::Some(B::Some(5))); // ==> b is B::Some(5) 507 | ``` 508 | 509 | --- 510 | ## Lifetimes 511 | 512 | - There's one more piece to the ownership puzzle: Lifetimes. 513 | - Lifetimes generally have a pretty steep learning curve. 514 | - We may cover them again later on in the course under a broader scope if 515 | necessary. 516 | - Don't worry if you don't understand these right away. 517 | 518 | --- 519 | ## Lifetimes 520 | 521 | - Imagine This: 522 | 1. I acquire a resource. 523 | 2. I lend you a reference to my resource. 524 | 3. I decide that I'm done with the resource, so I deallocate it. 525 | 4. You still hold a reference to the resource, and decide to use it. 526 | 5. You crash 😿. 527 | - We've already said that Rust makes this scenario impossible, but glossed over 528 | how. 529 | - We need to prove to the compiler that _step 3_ will never happen before _step 4_. 530 | 531 | --- 532 | ## Lifetimes 533 | 534 | - Ordinarily, references have an implicit lifetime that we don't need to care 535 | about: 536 | ```rust 537 | fn foo(x: &i32) { 538 | // ... 539 | } 540 | ``` 541 | - However, we can explicitly provide one instead: 542 | ```rust 543 | fn bar<'a>(x: &'a i32) { 544 | // ... 545 | } 546 | ``` 547 | 548 | - `'a`, pronounced "tick-a" or "the lifetime *a*" is a *named* lifetime 549 | parameter. 550 | - `<'a>` declares generic parameters, including lifetime parameters. 551 | - The type `&'a i32` is a reference to an `i32` that lives at least as 552 | long as the lifetime `'a`. 553 | 554 | ??? 555 | 556 | ## Stop here briefly to discuss 557 | 558 | --- 559 | ## Lifetimes 560 | 561 | - The compiler is smart enough not to need `'a` above, but this isn't always the 562 | case. 563 | - Scenarios that involve multiple references or returning references often 564 | require explicit lifetimes. 565 | - Speaking of which... 566 | 567 | --- 568 | ## Multiple Lifetime Parameters 569 | 570 | ```rust 571 | fn borrow_x_or_y<'a>(x: &'a str, y: &'a str) -> &'a str; 572 | ``` 573 | 574 | - In `borrow_x_or_y`, all input/output references all have the same lifetime. 575 | - `x` and `y` are borrowed (the reference is alive) as long as the returned 576 | reference exists. 577 | 578 | ```rust 579 | fn borrow_p<'a, 'b>(p: &'a str, q: &'b str) -> &'a str; 580 | ``` 581 | 582 | - In `borrow_p`, the output reference has the same lifetime as `p`. 583 | - `q` has a separate lifetime with no constrained relationship to `p`. 584 | - `p` is borrowed as long as the returned reference exists. 585 | 586 | --- 587 | ## Lifetimes 588 | 589 | - Okay, great, but what does this all mean? 590 | - If a reference `R` has a lifetime `'a`, it is _guaranteed_ that it will not 591 | outlive the owner of its underlying data (the value at `*R`) 592 | - If a reference `R` has a lifetime of `'a`, anything else with the lifetime 593 | `'a` is _guaranteed_ to live as long `R`. 594 | - This will probably become more clear the more you use lifetimes yourself. 595 | 596 | --- 597 | ## Lifetimes - `struct`s 598 | 599 | - Structs (and struct members) can have lifetime parameters. 600 | 601 | ```rust 602 | struct Pizza(Vec); 603 | struct PizzaSlice<'a> { 604 | pizza: &'a Pizza, // <- references in structs must 605 | index: u32, // ALWAYS have explicit lifetimes 606 | } 607 | 608 | let p1 = Pizza(vec![1, 2, 3, 4]); 609 | { 610 | let s1 = PizzaSlice { pizza: &p1, index: 2 }; // this is okay 611 | } 612 | 613 | let s2; 614 | { 615 | let p2 = Pizza(vec![1, 2, 3, 4]); 616 | s2 = PizzaSlice { pizza: &p2, index: 2 }; 617 | // no good - why? 618 | } 619 | ``` 620 | 621 | ??? 622 | 623 | ## Live demo! 624 | 625 | --- 626 | ## Lifetimes - `struct`s 627 | 628 | - Lifetimes can be constrained to "outlive" others. 629 | - Same syntax as type constraint: `<'b: 'a>`. 630 | 631 | ```rust 632 | struct Pizza(Vec); 633 | struct PizzaSlice<'a> { pizza: &'a Pizza, index: u32 } 634 | struct PizzaConsumer<'a, 'b: 'a> { // says "b outlives a" 635 | slice: PizzaSlice<'a>, // <- currently eating this one 636 | pizza: &'b Pizza, // <- so we can get more pizza 637 | } 638 | 639 | fn get_another_slice(c: &mut PizzaConsumer, index: u32) { 640 | c.slice = PizzaSlice { pizza: c.pizza, index: index }; 641 | } 642 | 643 | let p = Pizza(vec![1, 2, 3, 4]); 644 | { 645 | let s = PizzaSlice { pizza: &p, index: 1 }; 646 | let mut c = PizzaConsumer { slice: s, pizza: &p }; 647 | get_another_slice(&mut c, 2); 648 | } 649 | ``` 650 | 651 | --- 652 | ## Lifetimes - `'static` 653 | 654 | - There is one reserved, special lifetime, named `'static`. 655 | - `'static` means that a reference may be kept (and will be valid) for the 656 | lifetime of the entire program. 657 | - i.e. the data referred to will never go out of scope. 658 | - All `&str` literals have the `'static` lifetime. 659 | 660 | ```rust 661 | let s1: &str = "Hello"; 662 | let s2: &'static str = "World"; 663 | ``` 664 | 665 | --- 666 | ### Structured Data With Lifetimes 667 | 668 | - Any struct or enum that contains a reference must have an explicit lifetime. 669 | - Normal lifetime rules otherwise apply. 670 | 671 | ```rust 672 | struct Foo<'a, 'b> { 673 | v: &'a Vec, 674 | s: &'b str, 675 | } 676 | ``` 677 | 678 | --- 679 | ### Lifetimes in `impl` Blocks 680 | 681 | - Implementing methods on `Foo` struct requires lifetime annotations too! 682 | - You can read this block as "the implementation using the lifetimes `'a` and 683 | `'b` for the struct `Foo` using the lifetimes `'a` and `'b`." 684 | 685 | ```rust 686 | impl<'a, 'b> Foo<'a, 'b> { 687 | fn new(v: &'a Vec, s: &'b str) -> Foo<'a, 'b> { 688 | Foo { 689 | v: v, 690 | s: s, 691 | } 692 | } 693 | } 694 | ``` 695 | -------------------------------------------------------------------------------- /02/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "02 - Structured Data" 4 | --- 5 | -------------------------------------------------------------------------------- /03/content.md: -------------------------------------------------------------------------------- 1 | # Generics & Traits 2 | 3 | ### CIS 198 Lecture 3 4 | 5 | --- 6 | ## Generics 7 | 8 | - Suppose we simplify the `Resultish` enum from last week a bit... 9 | 10 | ```rust 11 | enum Result { 12 | Ok(String), 13 | Err(String), 14 | } 15 | ``` 16 | - Better, but it's still limited to passing two values which are both `String`s. 17 | 18 | --- 19 | ## Generics 20 | 21 | - This looks a lot like a standard library enum, `Result`: 22 | 23 | ```rust 24 | enum Result { 25 | Ok(T), 26 | Err(E), 27 | } 28 | ``` 29 | - `T` and `E` stand in for any generic type, not only `String`s. 30 | - You can use any CamelCase identifier for generic types. 31 | 32 | --- 33 | ## Generic Structs 34 | 35 | - Let's take a look at generic versions of several other structs from last 36 | week: 37 | 38 | ```rust 39 | struct Point { 40 | x: T, 41 | y: T, 42 | } 43 | 44 | enum List { 45 | Nil, 46 | Cons(T, Box>), 47 | } 48 | ``` 49 | 50 | --- 51 | ## Generic Implementations 52 | 53 | - To define implementations for structs & enums with generic types, declare the generics at the 54 | beginning of the `impl` block: 55 | 56 | ``` rust 57 | impl Result { 58 | fn is_ok(&self) -> bool { 59 | match *self { 60 | Ok(_) => true, 61 | Err(_) => false, 62 | } 63 | } 64 | } 65 | ``` 66 | 67 | --- 68 | ## Traits 69 | 70 | - Implementing functions on a per-type basis to pretty-print, compute equality, etc. is 71 | fine, but unstructured. 72 | - We currently have no abstract way to reason about what types can do what! 73 | 74 | ```rust 75 | struct Point { 76 | x: i32, 77 | y: i32, 78 | } 79 | 80 | impl Point { 81 | fn format(&self) -> String { 82 | format!("({}, {})", self.x, self.y) 83 | } 84 | 85 | fn equals(&self, other: Point) -> bool { 86 | self.x == other.x && self.y == other.y 87 | } 88 | } 89 | ``` 90 | 91 | --- 92 | ## Traits 93 | 94 | - Solution: Traits (coming right now)! 95 | - Like we say every week, these are similar to Java interfaces or Haskell 96 | typeclasses 97 | 98 | --- 99 | ## Traits 100 | 101 | - To define a trait, use a `trait` block, which gives function definitions for 102 | the required methods. 103 | - This is not the same as an `impl` block. 104 | - Mostly only contains method signatures without definitions. 105 | 106 | ```rust 107 | trait PrettyPrint { 108 | fn format(&self) -> String; 109 | } 110 | ``` 111 | 112 | --- 113 | ## Traits 114 | 115 | - To implement a trait, use an `impl Trait for Type` block. 116 | - All methods specified by the trait must be implemented. 117 | - One impl block per type per trait. 118 | - You can use `self`/`&self` inside the trait `impl` block as usual. 119 | 120 | ```rust 121 | struct Point { 122 | x: i32, 123 | y: i32, 124 | } 125 | 126 | impl PrettyPrint for Point { 127 | fn format(&self) -> String { 128 | format!("({}, {})", self.x, self.y) 129 | } 130 | } 131 | ``` 132 | 133 | --- 134 | ## Generic Functions 135 | 136 | - You can make a function generic over types as well. 137 | - `` declares the type parameters for `foo`. 138 | - `x: T, y: U` uses those type parameters. 139 | - You can read this as "the function `foo`, for all types `T` and `U`, 140 | of two arguments: `x` of type `T` and `y` of type `U`." 141 | 142 | ```rust 143 | fn foo(x: T, y: U) { 144 | // ... 145 | } 146 | ``` 147 | 148 | --- 149 | ## Generics with Trait Bounds 150 | 151 | - Instead of allowing _literally any_ type, you can constrain generic types by 152 | _trait bounds_. 153 | - This gives more power to generic functions & types. 154 | - Trait bounds can be specified with `T: SomeTrait` or with a `where` clause. 155 | - "where `T` is `Clone`" 156 | 157 | ```rust 158 | fn cloning_machine(t: T) -> (T, T) { 159 | (t.clone(), t.clone()) 160 | } 161 | 162 | fn cloning_machine_2(t: T) -> (T, T) 163 | where T: Clone { 164 | (t.clone(), t.clone()) 165 | } 166 | ``` 167 | 168 | --- 169 | ## Generics with Trait Bounds 170 | 171 | - Multiple trait bounds are specified like `T: Clone + Ord`. 172 | - There's no way (yet) to specify [negative trait bounds](https://internals.rust-lang.org/t/pre-rfc-mutually-exclusive-traits/2126). 173 | - e.g. you can't stipulate that a `T` must not be `Clone`. 174 | 175 | ```rust 176 | fn clone_and_compare(t1: T, t2: T) -> bool { 177 | t1.clone() > t2.clone() 178 | } 179 | ``` 180 | 181 | --- 182 | ## Generic Types With Trait Bounds 183 | 184 | - You can also define structs with generic types and trait bounds. 185 | - Be sure to declare all of your generic types in the struct header _and_ the 186 | impl block header. 187 | - Only the impl block header needs to specify trait bounds. 188 | - This is useful if you want to have multiple impls for a struct each with 189 | different trait bounds 190 | 191 | --- 192 | ## Generic Types With Trait Bounds 193 | 194 | ```rust 195 | enum Result { 196 | Ok(T), 197 | Err(E), 198 | } 199 | 200 | trait PrettyPrint { 201 | fn format(&self) -> String; 202 | } 203 | 204 | impl PrettyPrint for Result { 205 | fn format(&self) -> String { 206 | match *self { 207 | Ok(t) => format!("Ok({})", t.format()), 208 | Err(e) => format!("Err({})", e.format()), 209 | } 210 | } 211 | } 212 | ``` 213 | 214 | --- 215 | ## Examples: Equality 216 | 217 | ```rust 218 | enum Result { Ok(T), Err(E), } 219 | 220 | // This is not the trait Rust actually uses for equality 221 | trait Equals { 222 | fn equals(&self, other: &Self) -> bool; 223 | } 224 | 225 | impl Equals for Result { 226 | fn equals(&self, other: &Self) -> bool { 227 | match (*self, *other) { 228 | Ok(t1), Ok(t2) => t1.equals(t2), 229 | Err(e1), Err(e2) => e1.equals(e2), 230 | _ => false 231 | } 232 | } 233 | } 234 | ``` 235 | - `Self` is a special type which refers to the type of `self`. 236 | 237 | --- 238 | ## Inheritance 239 | 240 | - Some traits may require other traits to be implemented first. 241 | - e.g., `Eq` requires that `PartialEq` be implemented, and `Copy` requires `Clone`. 242 | - Implementing the `Child` trait below requires you to also implement `Parent`. 243 | 244 | ```rust 245 | trait Parent { 246 | fn foo(&self) { 247 | // ... 248 | } 249 | } 250 | 251 | trait Child: Parent { 252 | fn bar(&self) { 253 | self.foo(); 254 | // ... 255 | } 256 | } 257 | ``` 258 | 259 | --- 260 | ## Default Methods 261 | 262 | - Traits can have default implementations for methods! 263 | - Useful if you have an idea of how an implementor will commonly define a trait method. 264 | - When a default implementation is provided, the implementor of the trait doesn't need to define that method. 265 | - Define default implementations of trait methods by simply writing the body in 266 | the `trait` block. 267 | 268 | ```rust 269 | trait PartialEq { 270 | fn eq(&self, other: &Rhs) -> bool; 271 | 272 | fn ne(&self, other: &Rhs) -> bool { 273 | !self.eq(other) 274 | } 275 | } 276 | 277 | trait Eq: PartialEq {} 278 | ``` 279 | 280 | --- 281 | ## Default Methods 282 | 283 | - Implementors of the trait can overwrite default implementations, but make sure 284 | you have a good reason to! 285 | - e.g., _never_ define `ne` so that it violates the relationship between 286 | `eq` and `ne`. 287 | 288 | --- 289 | ## Deriving 290 | 291 | - Many traits are so straightforward that the compiler can often implement them 292 | for you. 293 | - A `#[derive(...)]` attribute tells the compiler to insert a default 294 | implementation for whatever traits you tell it to. 295 | - This removes the tedium of repeatedly manually implementing traits like `Clone` yourself! 296 | 297 | ```rust 298 | #[derive(Eq, PartialEq, Debug)] 299 | enum Result { 300 | Ok(T), 301 | Err(E) 302 | } 303 | ``` 304 | 305 | --- 306 | ## Deriving 307 | 308 | - You can only do this for the following core traits: 309 | - `Clone`, `Copy`, `Debug`, `Default`, `Eq`, 310 | - `Hash`, `Ord`, `PartialEq`, `PartialOrd`. 311 | - Deriving custom traits is an unstable feature as of Rust 1.6. 312 | - Careful: deriving a trait won't always work. 313 | - Can only derive a trait on a data type when all of its members can have derived the trait. 314 | - e.g., `Eq` can't be derived on a struct containing only `f32`s, since 315 | `f32` is not `Eq`. 316 | 317 | --- 318 | ## Core traits 319 | 320 | - It's good to be familiar with the core traits. 321 | - `Clone`, `Copy` 322 | - `Debug` 323 | - `Default` 324 | - `Eq`, `PartialEq` 325 | - `Hash` 326 | - `Ord`, `PartialOrd` 327 | 328 | --- 329 | ### Clone 330 | 331 | ```rust 332 | pub trait Clone: Sized { 333 | fn clone(&self) -> Self; 334 | 335 | fn clone_from(&mut self, source: &Self) { ... } 336 | } 337 | ``` 338 | - A trait which defines how to duplicate a value of type `T`. 339 | - This can solve ownership problems. 340 | - You can clone an object rather than taking ownership or borrowing! 341 | 342 | --- 343 | ### Clone 344 | 345 | ```rust 346 | #[derive(Clone)] // without this, Bar cannot derive Clone. 347 | struct Foo { 348 | x: i32, 349 | } 350 | 351 | #[derive(Clone)] 352 | struct Bar { 353 | x: Foo, 354 | } 355 | ``` 356 | 357 | --- 358 | ### Copy 359 | ```rust 360 | pub trait Copy: Clone { } 361 | ``` 362 | - `Copy` denotes that a type has "copy semantics" instead of "move semantics." 363 | - Type must be able to be copied by copying bits (`memcpy`). 364 | - Types that contain references _cannot_ be `Copy`. 365 | - Marker trait: does not implement any methods, but defines behavior instead. 366 | - In general, if a type _can_ be `Copy`, it _should_ be `Copy`. 367 | 368 | --- 369 | ### Debug 370 | 371 | ```rust 372 | pub trait Debug { 373 | fn fmt(&self, &mut Formatter) -> Result; 374 | } 375 | ``` 376 | 377 | - Defines output for the `{:?}` formatting option. 378 | - Generates debug output, not pretty printed. 379 | - Generally speaking, you should always derive this trait. 380 | 381 | ```rust 382 | #[derive(Debug)] 383 | struct Point { 384 | x: i32, 385 | y: i32, 386 | } 387 | 388 | let origin = Point { x: 0, y: 0 }; 389 | println!("The origin is: {:?}", origin); 390 | // The origin is: Point { x: 0, y: 0 } 391 | ``` 392 | 393 | --- 394 | ### Default 395 | 396 | ```rust 397 | pub trait Default: Sized { 398 | fn default() -> Self; 399 | } 400 | ``` 401 | - Defines a default value for a type. 402 | 403 | --- 404 | ### Eq vs. PartialEq 405 | 406 | ```rust 407 | pub trait PartialEq { 408 | fn eq(&self, other: &Rhs) -> bool; 409 | 410 | fn ne(&self, other: &Rhs) -> bool { ... } 411 | } 412 | 413 | pub trait Eq: PartialEq {} 414 | ``` 415 | - Traits for defining equality via the `==` operator. 416 | 417 | --- 418 | ### Eq vs. PartialEq 419 | 420 | - `PartialEq` represents a _partial equivalence relation_. 421 | - Symmetric: if a == b then b == a 422 | - Transitive: if a == b and b == c then a == c 423 | - `ne` has a default implementation in terms of `eq`. 424 | - `Eq` represents a _total equivalence relation_. 425 | - Symmetric: if a == b then b == a 426 | - Transitive: if a == b and b == c then a == c 427 | - **Reflexive: a == a** 428 | - `Eq` does not define any additional methods. 429 | - (It is also a Marker trait.) 430 | 431 | --- 432 | ### Hash 433 | 434 | ```rust 435 | pub trait Hash { 436 | fn hash(&self, state: &mut H); 437 | 438 | fn hash_slice(data: &[Self], state: &mut H) 439 | where Self: Sized { ... } 440 | } 441 | ``` 442 | - A hashable type. 443 | - The `H` type parameter is an abstract hash state used to compute the hash. 444 | - If you also implement `Eq`, there is an additional, important property: 445 | ```rust 446 | k1 == k2 -> hash(k1) == hash(k2) 447 | ``` 448 | 449 | ¹taken from Rustdocs 450 | 451 | --- 452 | ### Ord vs. PartialOrd 453 | 454 | ```rust 455 | pub trait PartialOrd: PartialEq { 456 | // Ordering is one of Less, Equal, Greater 457 | fn partial_cmp(&self, other: &Rhs) -> Option; 458 | 459 | fn lt(&self, other: &Rhs) -> bool { ... } 460 | fn le(&self, other: &Rhs) -> bool { ... } 461 | fn gt(&self, other: &Rhs) -> bool { ... } 462 | fn ge(&self, other: &Rhs) -> bool { ... } 463 | } 464 | ``` 465 | - Traits for values that can be compared for a sort-order. 466 | 467 | --- 468 | ### Ord vs. PartialOrd 469 | 470 | - The comparison must satisfy, for all `a`, `b` and `c`: 471 | - Antisymmetry: if `a < b` then `!(a > b)`, as well as `a > b` implying `!(a < b)`; and 472 | - Transitivity: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. 473 | - `lt`, `le`, `gt`, `ge` have default implementations based on `partial_cmp`. 474 | 475 | ¹taken from Rustdocs 476 | 477 | --- 478 | ### Ord vs. PartialOrd 479 | 480 | ```rust 481 | pub trait Ord: Eq + PartialOrd { 482 | fn cmp(&self, other: &Self) -> Ordering; 483 | } 484 | ``` 485 | - Trait for types that form a total order. 486 | - An order is a total order if it is (for all `a`, `b` and `c`): 487 | - total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true; and 488 | - transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. 489 | - When this trait is derived, it produces a lexicographic ordering. 490 | 491 | ¹taken from Rustdocs 492 | 493 | --- 494 | ## Associated Types 495 | 496 | - Take this `Graph` trait from the Rust book: 497 | 498 | ```rust 499 | trait Graph { 500 | fn edges(&self, &N) -> Vec; 501 | // etc 502 | } 503 | ``` 504 | 505 | - `N` and `E` are generic type parameters, but they don't have any meaningful 506 | association to `Graph` 507 | - Also, any function that takes a `Graph` must also be generic over `N` and `E`! 508 | 509 | ```rust 510 | fn distance>(graph: &G, start: &N, end: &N) 511 | -> u32 { /*...*/ } 512 | ``` 513 | 514 | --- 515 | ## Associated Types 516 | 517 | - Solution: associated types! 518 | - `type` definitions inside a trait block indicate associated generic types on 519 | the trait. 520 | - An implementor of the trait may specify what the associated types correspond 521 | to. 522 | 523 | ```rust 524 | trait Graph { 525 | type N; 526 | type E; 527 | 528 | fn edges(&self, &Self::N) -> Vec; 529 | } 530 | 531 | impl Graph for MyGraph { 532 | type N = MyNode; 533 | type E = MyEdge; 534 | 535 | fn edges(&self, n: &MyNode) -> Vec { /*...*/ } 536 | } 537 | ``` 538 | 539 | --- 540 | ## Associated Types 541 | 542 | - For example, in the standard library, traits like `Iterator` define an `Item` associated type. 543 | - Methods on the trait like `Iterator::next` then return an `Option`! 544 | - This lets you easily specify what type a client gets by iterating over 545 | your collection. 546 | 547 | --- 548 | ## Trait Scope 549 | 550 | - Say our program defines some trait `Foo`. 551 | - It's possible to implement this trait on any type in Rust, including types that 552 | you don't own: 553 | 554 | ```rust 555 | trait Foo { 556 | fn bar(&self) -> bool; 557 | } 558 | 559 | impl Foo for i32 { 560 | fn bar(&self) -> bool { 561 | true 562 | } 563 | } 564 | ``` 565 | 566 | - But this is really bad practice. Avoid if you can! 567 | 568 | --- 569 | ## Trait Scope 570 | 571 | - The scope rules for implementing traits: 572 | - You need to `use` a trait in order to access its methods on types, even if 573 | you have access to the type. 574 | - In order to write an `impl`, you need to own (i.e. have yourself defined) 575 | either the trait or the type. 576 | 577 | --- 578 | ### Display 579 | 580 | ```rust 581 | pub trait Display { 582 | fn fmt(&self, &mut Formatter) -> Result<(), Error>; 583 | } 584 | 585 | impl Display for Point { 586 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 587 | write!(f, "Point {}, {})", self.x, self.y) 588 | } 589 | } 590 | ``` 591 | 592 | - Defines output for the `{}` formatting option. 593 | - Like Debug, but should be pretty printed. 594 | - No standard output and cannot be derived! 595 | - You can use `write!` macro to implement this without using Formatter. 596 | 597 | --- 598 | ## Addendum: Drop 599 | 600 | ```rust 601 | pub trait Drop { 602 | fn drop(&mut self); 603 | } 604 | ``` 605 | 606 | - A trait for types that are destructable (which is all types). 607 | - `Drop` requires one method, `drop`, but you should never call this method yourself. 608 | - It's inserted automatically by the compiler when necessary. 609 | 610 | --- 611 | ## Addendum: Drop 612 | 613 | - Typically, you won't actually implement `Drop` for a type 614 | - Generally the default implementation is fine. 615 | - You also don't need to `derive` `Drop` either. 616 | - Why implement `Drop` then? 617 | - If you need some special behavior when an object gets destructed. 618 | 619 | --- 620 | ## Addendum: Drop 621 | 622 | - Example: Rust's reference-counted pointer type `Rc` has special `Drop` rules: 623 | - If the number of references to an `Rc` pointer is greater than 1, `drop` decrements the ref count. 624 | - The `Rc` is actually deleted when the reference count drops to 0. 625 | 626 | --- 627 | ## Addendum: `Sized` vs. `?Sized` 628 | 629 | - `Sized` indicates that a type has a constant size known at compile time! 630 | - Its evil twin, `?Sized`, indicates that a type _might_ be sized. 631 | - By default, all types are implicitly `Sized`, and `?Sized` undoes this. 632 | - Types like `[T]` and `str` (no `&`) are `?Sized`. 633 | - For example, `Box` allows `T: ?Sized`. 634 | - You rarely interact with these traits directly, but they show up a lot in trait bounds. 635 | 636 | --- 637 | ## Trait Objects 638 | 639 | - Consider the following trait, and its implementors: 640 | 641 | ```rust 642 | trait Foo { fn bar(&self); } 643 | 644 | impl Foo for String { 645 | fn bar(&self) { /*...*/ } 646 | } 647 | 648 | impl Foo for usize { 649 | fn bar(&self) { /*...*/ } 650 | } 651 | ``` 652 | 653 | --- 654 | ## Trait Objects 655 | 656 | - We can call either of these versions of `bar` via static dispatch using any type with bounds `T: Foo`. 657 | - When this code is compiled, the compiler will insert calls to specialized versions of `bar` 658 | - One function is generated for each implementor of the `Foo` trait. 659 | 660 | ```rust 661 | fn blah(x: T) where T: Foo { 662 | x.bar() 663 | } 664 | 665 | fn main() { 666 | let s = "Foo".to_string(); 667 | let u = 12; 668 | 669 | blah(s); 670 | blah(u); 671 | } 672 | ``` 673 | 674 | --- 675 | ## Trait Objects 676 | 677 | - It is also possible to have Rust perform _dynamic_ dispatch through the use of *trait objects*. 678 | - A trait object is something like `Box` or `&Foo` 679 | - The data behind the reference/box must implement the trait `Foo`. 680 | - The concrete type underlying the trait is erased; it can't be determined. 681 | 682 | --- 683 | ## Trait Objects 684 | 685 | ```rust 686 | trait Foo { /*...*/ } 687 | 688 | impl Foo for char { /*...*/ } 689 | impl Foo for i32 { /*...*/ } 690 | 691 | fn use_foo(f: &Foo) { 692 | // No way to figure out if we got a `char` or an `i32` 693 | // or anything else! 694 | match *f { 695 | // What type do we have? I dunno... 696 | // error: mismatched types: expected `Foo`, found `_` 697 | 198 => println!("CIS 198!"), 698 | 'c' => println!("See?"), 699 | _ => println!("Something else..."), 700 | } 701 | } 702 | 703 | use_foo(&'c'); // These coerce into `&Foo`s 704 | use_foo(&198i32); 705 | ``` 706 | 707 | --- 708 | ## Trait Objects 709 | 710 | - When a trait object is used, method dispatch must be performed at runtime. 711 | - The compiler can't know the type underlying the trait reference, since it was erased. 712 | - This causes a runtime penalty, but is useful when handling things like dynamically sized types. 713 | 714 | --- 715 | ## Object Safety 716 | 717 | - Not all traits can be safely used in trait objects! 718 | - Trying to create a variable of type `&Clone` will cause a compiler error, as `Clone` is not _object safe_. 719 | - A trait is object-safe if: 720 | - It does not require that `Self: Sized` 721 | - Its methods must not use `Self` 722 | - Its methods must not have any type parameters 723 | - Its methods do not require that `Self: Sized` 724 | 725 | ¹taken from Rustdocs 726 | 727 | --- 728 | ### Addendum: Generics With Lifetime Bounds 729 | 730 | - Some generics may have lifetime bounds like `T: 'a`. 731 | - Semantically, this reads as "Type `T` must live at least as long as the lifetime `'a`." 732 | - Why is this useful? 733 | 734 | --- 735 | ### Addendum: Generics With Lifetime Bounds 736 | 737 | - Imagine you have some collection of type `T`. 738 | - If you iterate over this collection, you should be able to guarantee that 739 | everything in it lives as long as the collection. 740 | - If you couldn't, Rust wouldn't be safe! 741 | - `std::Iterator` structs usually contain these sorts of constraints. 742 | -------------------------------------------------------------------------------- /03/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "03 - Generics & Traits" 4 | --- 5 | -------------------------------------------------------------------------------- /04/content.md: -------------------------------------------------------------------------------- 1 | # Closures 2 | 3 | ### CIS 198 Lecture 4 4 | 5 | --- 6 | ## Closures 7 | - A closure, anonymous function, or lambda function is a common paradigm in 8 | functional languages. 9 | - In Rust, they're fairly robust, and match up well with the rest of Rust's 10 | ownership model. 11 | 12 | ```rust 13 | let square = |x: i32| -> i32 { x * x }; 14 | println!("{}", square(3)); 15 | // => 6 16 | ``` 17 | 18 | ??? 19 | 20 | Inline function definitions which can be bound to variables. The function block 21 | is executed when the closure is called. 22 | 23 | --- 24 | ## Closure Syntax 25 | 26 | ```rust 27 | let foo_v1 = |x: i32| { x * x }; 28 | let foo_v2 = |x: i32, y: i32| x * y; 29 | let foo_v3 = |x: i32| { 30 | // Very Important Arithmetic 31 | let y = x * 2; 32 | let z = 4 + y; 33 | x + y + z 34 | }; 35 | let foo_v4 = |x: i32| if x == 0 { 0 } else { 1 }; 36 | ``` 37 | 38 | - These look pretty similar to function definitions. 39 | - Specify arguments in `||`, followed by the return expression. 40 | - The return expression can be a series of expressions in `{}`. 41 | 42 | ??? 43 | 44 | - `let` instead of `fn` 45 | - Arguments in pipes 46 | - Braces are optional 47 | 48 | --- 49 | ## Type Inference 50 | 51 | ```rust 52 | let square_v4 = |x: u32| { (x * x) as i32 }; 53 | 54 | let square_v4 = |x| -> i32 { x * x }; // ← unable to infer enough 55 | let square_v4 = |x| { x * x }; // ← type information! 56 | ``` 57 | 58 | - Unlike functions, we don't _need_ to specify the return type or argument types 59 | of a closure. 60 | - In this case, the compiler can't infer the type of the argument `x` from 61 | the return expression `x * x`. 62 | 63 | ??? 64 | 65 | Having concrete function types for type inference and self-documentation. For 66 | closures, ease of use is more important. 67 | 68 | --- 69 | ## Closure Environment 70 | 71 | - Closures _close_ over (contain) their environment. 72 | 73 | ```rust 74 | let magic_num = 5; 75 | let magic_johnson = 32; 76 | let plus_magic = |x: i32| x + magic_num; 77 | ``` 78 | 79 | - The closure `plus_magic` is able to reference `magic_num` even though it's not 80 | passed as an argument. 81 | - `magic_num` is in the "environment" of the closure. 82 | - `magic_johnson` is not borrowed! 83 | 84 | --- 85 | ## Closure Environment 86 | 87 | - If we try to borrow `magic_num` in a conflicting way after the 88 | closure is bound, we'll get an error from the compiler: 89 | 90 | ```rust 91 | let mut magic_num = 5; 92 | let magic_johnson = 32; 93 | let plus_magic = |x: i32| x + magic_num; 94 | 95 | let more_magic = &mut magic_num; // Err! 96 | println!("{}", magic_johnson); // Ok! 97 | ``` 98 | 99 | ``` 100 | error: cannot borrow `magic_num` as mutable because it is 101 | already borrowed as immutable 102 | 103 | [...] the immutable borrow prevents subsequent moves or mutable 104 | borrows of `magic_num` until the borrow ends 105 | ``` 106 | 107 | - Why? `plus_magic` borrows `magic_num` when it closes over it! 108 | - However, `magic_johnson` is not used in the closure, and its ownership is not 109 | affected. 110 | 111 | --- 112 | ## Closure Environment 113 | 114 | - We can fix this kind of problem by making the closure go out of scope: 115 | 116 | ```rust 117 | let mut magic_num = 5; 118 | { 119 | let plus_magic = |x: i32| x + magic_num; 120 | } // the borrow of magic_num ends here 121 | 122 | let more_magic = &mut magic_num; // Ok! 123 | println!("magic_num: {}", more_magic); 124 | ``` 125 | 126 | ??? 127 | 128 | Questions? 129 | 130 | --- 131 | ## Move Closures 132 | 133 | - As usual, closures are choose-your-own-~~adventure~~ ownership. 134 | - Sometimes it's not okay to have a closure borrow _anything_. 135 | - You can force a closure to _take ownership_ of all environment 136 | variables by using the `move` keyword. 137 | - "Taking ownership" can mean taking a copy, not just moving. 138 | 139 | ```rust 140 | let mut magic_num = 5; 141 | let own_the_magic = move |x: i32| x + magic_num; 142 | let more_magic = &mut magic_num; 143 | ``` 144 | 145 | --- 146 | ## Move Closures 147 | 148 | - `move` closures are necessary when the closure `f` needs to outlive the scope in 149 | which it was created. 150 | - e.g. when you pass `f` into a thread, or return `f` from a function. 151 | - `move` essentially _disallows_ bringing references into the closure. 152 | 153 | ```rust 154 | fn make_closure(x: i32) -> Box i32> { 155 | let f = move |y| x + y; // ^ more on this in 15 seconds 156 | Box::new(f) 157 | } 158 | 159 | let f = make_closure(2); 160 | println!("{}", f(3)); 161 | ``` 162 | 163 | --- 164 | ## Closure Ownership 165 | 166 | - Sometimes, a closure _must_ take ownership of an environment variable to be 167 | valid. This happens automatically (without `move`): 168 | 169 | - If the value is moved into the return value. 170 | ```rust 171 | let lottery_numbers = vec![11, 39, 51, 57, 75]; 172 | { 173 | let ticket = || { lottery_numbers }; 174 | } 175 | // The braces do no good here. 176 | println!("{:?}", lottery_numbers); // use of moved value 177 | ``` 178 | 179 | - Or moved anywhere else. 180 | ```rust 181 | let numbers = vec![2, 5, 32768]; 182 | let alphabet_soup = || { numbers; vec!['a', 'b'] }; 183 | // ^ throw away unneeded ingredients 184 | println!("{:?}", numbers); // use of moved value 185 | ``` 186 | 187 | - If the type is not `Copy`, the original variable is invalidated. 188 | 189 | --- 190 | ## Closure Ownership 191 | 192 | ```rust 193 | let numbers = vec![2, 5, 32768]; 194 | let alphabet_soup = || { numbers; vec!['a', 'b'] }; 195 | // ^ throw away unneeded ingredients 196 | alphabet_soup(); 197 | alphabet_soup(); // use of moved value 198 | ``` 199 | 200 | - Closures which own data and then move it can only be called once. 201 | - `move` behavior is implicit because `alphabet_soup` must own `numbers` to 202 | move it. 203 | 204 | ```rust 205 | let numbers = vec![2, 5, 32768]; 206 | let alphabet_soup = move || { println!("{:?}", numbers) }; 207 | alphabet_soup(); 208 | alphabet_soup(); // Delicious soup 209 | ``` 210 | 211 | - Closures which own data but don't move it can be called multiple times. 212 | 213 | --- 214 | ## Closure Ownership 215 | 216 | - The same closure can take some values by reference and others by moving 217 | ownership (or Copying values), determined by behavior. 218 | 219 | --- 220 | ## Closure Traits 221 | 222 | - Closures are actually based on a set of traits under the hood! 223 | - `Fn`, `FnMut`, `FnOnce` - method calls are overloadable operators. 224 | 225 | ```rust 226 | pub trait Fn : FnMut { 227 | extern "rust-call" 228 | fn call(&self, args: Args) -> Self::Output; 229 | } 230 | 231 | pub trait FnMut : FnOnce { 232 | extern "rust-call" 233 | fn call_mut(&mut self, args: Args) -> Self::Output; 234 | } 235 | 236 | pub trait FnOnce { 237 | type Output; 238 | 239 | extern "rust-call" 240 | fn call_once(self, args: Args) -> Self::Output; 241 | } 242 | ``` 243 | 244 | --- 245 | ## Closure Traits 246 | 247 | - These traits all look pretty similar, but differ in the way they take `self`: 248 | - `Fn` borrows `self` as `&self` 249 | - `FnMut` borrows `self` mutably as `&mut self` 250 | - `FnOnce` takes ownership of `self` 251 | - `Fn` is a superset of `FnMut`, which is a superset of `FnOnce`. 252 | - Functions also implement these traits. 253 | 254 | "The `|| {}` syntax for closures is sugar for these three traits. Rust will 255 | generate a struct for the environment, impl the appropriate trait, and then use 256 | it."¹ 257 | 258 | ¹Taken from the Rust Book 259 | 260 | --- 261 | ## Closures As Arguments 262 | 263 | - Passing closures works like function pointers. 264 | - Let's take a (simplified) look at Rust's definition for `map`¹. 265 | 266 | ```rust 267 | // self = Vec 268 | fn map(self, f: F) -> Vec 269 | where F: FnMut(A) -> B; 270 | ``` 271 | 272 | - `map` takes an argument `f: F`, where `F` is an `FnMut` trait object. 273 | - You can pass regular functions in, since the traits line up! 274 | 275 | ¹Real `map` coming in next lecture. 276 | 277 | --- 278 | ## Returning Closures 279 | 280 | - You may find it necessary to return a closure from a function. 281 | - Unfortunately, since closures are implicitly trait objects, they're unsized! 282 | 283 | ```rust 284 | fn i_need_some_closure() -> (Fn(i32) -> i32) { 285 | let local = 2; 286 | |x| x * local 287 | } 288 | ``` 289 | 290 | ``` 291 | error: the trait `core::marker::Sized` is not implemented 292 | for the type `core::ops::Fn(i32) -> i32 + 'static` 293 | ``` 294 | 295 | - An `Fn` object is not of constant size at compile time. 296 | - The compiler cannot properly reason about how much space to allocate for the `Fn`. 297 | 298 | --- 299 | ## Returning Closures 300 | 301 | - Okay, we can fix this! Just wrap the `Fn` in a layer of indirection and return a reference! 302 | 303 | ```rust 304 | fn i_need_some_closure_by_reference() -> &(Fn(i32) -> i32) { 305 | let local = 2; 306 | |x| x * local 307 | } 308 | ``` 309 | 310 | ``` 311 | error: missing lifetime specifier 312 | ``` 313 | 314 | - Now what? We haven't given this closure a lifetime specifier... 315 | - The reference we're returning must outlive this function. 316 | - But it can't, since that would create a dangling pointer. 317 | 318 | --- 319 | ## Returning Closures 320 | 321 | - What's the right way to fix this? Use a `Box`! 322 | 323 | ```rust 324 | fn box_me_up_that_closure() -> Box i32> { 325 | let local = 2; 326 | Box::new(|x| x * local) 327 | } 328 | ``` 329 | 330 | ``` 331 | error: closure may outlive the current function, but it 332 | borrows `local`, which is owned by the current function [E0373] 333 | ``` 334 | 335 | - Augh! We were so close! 336 | - The closure we're returning is still holding on to its environment. 337 | - That's bad, since once `box_me_up_that_closure` returns, `local` will be destroyed. 338 | 339 | --- 340 | ## Returning Closures 341 | - The good news? We already know how to fix this: 342 | 343 | ```rust 344 | fn box_up_your_closure_and_move_out() -> Box i32> { 345 | let local = 2; 346 | Box::new(move |x| x * local) 347 | } 348 | ``` 349 | 350 | - And you're done. It's elementary! 351 | -------------------------------------------------------------------------------- /04/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "04 - Closures" 4 | --- 5 | -------------------------------------------------------------------------------- /05/content.md: -------------------------------------------------------------------------------- 1 | # Standard Library 2 | 3 | ### CIS 198 Lecture 5 4 | 5 | --- 6 | ## String Types 7 | 8 | - Rust strings are complicated. 9 | - Sequences of Unicode values encoded in UTF-8. 10 | - Not null-terminated and may contain null bytes. 11 | - There are two kinds: `&str` and `String`. 12 | 13 | --- 14 | ## `&str` 15 | 16 | - `&str` is a string slice (like array slice). 17 | - `"string literals"` are of type `&str`.¹ 18 | - `&str`s are statically-allocated and fixed-size. 19 | - May not be indexed with `some_str[i]`, as each character may be multiple bytes 20 | due to Unicode. 21 | - Instead, iterate with `chars()`: 22 | - `for c in "1234".chars() { ... }` 23 | - As with all Rust references, they have an associated lifetime. 24 | 25 | ¹More specifically, they have the type `&'static str`. 26 | 27 | --- 28 | ## `String` 29 | 30 | - `String`s are heap-allocated, and are dynamically growable. 31 | - Like `Vec`s in that regard. 32 | - In fact, `String` is just a wrapper over `Vec`! 33 | - Cannot be indexed either. 34 | - You can select characters with `s.nth(i)`. 35 | - May be coerced into an `&str` by taking a reference to the `String`. 36 | 37 | ```rust 38 | let s0: String = String::new(); 39 | let s1: String = "foo".to_string(); 40 | let s2: String = String::from("bar"); 41 | let and_s: &str = &s0; 42 | ``` 43 | 44 | --- 45 | ## `str` 46 | 47 | - If `&str` is the second string type, what exactly is `str`? 48 | - An `Unsized` type, meaning the size is unknown at compile time. 49 | - You can't have bindings to `str`s directly, only references. 50 | 51 | --- 52 | ## String Concatenation 53 | 54 | - A `String` and an `&str` may be concatenated with `+`: 55 | 56 | ```rust 57 | let course_code = "CIS".to_string(); 58 | let course_name = course_code + " 198"; 59 | ``` 60 | 61 | - Concatenating two `String`s requires coercing one to `&str`: 62 | 63 | ```rust 64 | let course_code = String::from("CIS"); 65 | let course_num = String::from(" 198"); 66 | let course_name = course_code + &course_num; 67 | ``` 68 | 69 | - You can't concatenate two `&str`s. 70 | 71 | ```rust 72 | let course_name = "CIS " + "198"; // Err! 73 | ``` 74 | 75 | --- 76 | ## String Conversion 77 | 78 | - However, *actually* converting a `String` into an `&str` requires a 79 | dereference: 80 | 81 | ```rust 82 | use std::net::TcpStream; 83 | 84 | TcpStream::connect("192.168.0.1:3000"); // &str 85 | let addr = "192.168.0.1:3000".to_string(); 86 | TcpStream::connect(&*addr); 87 | ``` 88 | 89 | - This doesn't automatically coerce because `TcpStream` doesn't take an argument 90 | of type `&str`, but a Trait bounded type: 91 | - `TcpStream::connect(addr: A);` 92 | 93 | --- 94 | ### Aside: `Deref` Coercions 95 | 96 | - Rust's automatic dereferencing behavior works *between* types as well. 97 | 98 | ```rust 99 | pub trait Deref { 100 | type Target: ?Sized; 101 | fn deref(&self) -> &Self::Target; 102 | } 103 | ``` 104 | - Since `String` implements `Deref`, so values of `&String` will 105 | automatically be dereferenced to `&str` when possible. 106 | 107 | --- 108 | ## `String` & `&str`: Why? 109 | 110 | - Like slices for `Vec`s, `&str`s are useful for passing a view into a `String`. 111 | - It's expensive to copy a `String` around, and lending an entire `String` out 112 | may be overkill. 113 | - `&str` therefore allows you to pass portions of a `String` around, saving 114 | memory. 115 | 116 | --- 117 | ## `String` & `&str`: Why? 118 | - Generally, if you want to do more than use string literals, use `String`. 119 | - You can then lend out `&str`s easily. 120 | 121 | --- 122 | ## `Option` 123 | 124 | ```rust 125 | enum Option { 126 | None, 127 | Some(T), 128 | } 129 | ``` 130 | 131 | - Provides a concrete type to the concept of _nothingness_. 132 | - Use this instead of returning `NaN`, `-1`, `null`, etc. from a function. 133 | - No restrictions on what `T` may be. 134 | 135 | --- 136 | ### `Option::unwrap()` 137 | 138 | - The pattern where None values are ignored is pretty common: 139 | 140 | ```rust 141 | // fn foo() -> Option 142 | 143 | match foo() { 144 | None => None, 145 | Some(value) => { 146 | bar(value) 147 | // ... 148 | }, 149 | } 150 | ``` 151 | 152 | --- 153 | ### `Option::unwrap()` 154 | 155 | - What if we extracted the pattern match into a separate function to simplify it? 156 | 157 | ```rust 158 | fn unwrap(&self) -> T { // 🎁! 159 | match *self { 160 | None => panic!("Called `Option::unwrap()` on a `None` value"), 161 | Some(value) => value, 162 | } 163 | } 164 | 165 | let x = foo().unwrap(); 166 | let y = bar(x); 167 | // ... 168 | ``` 169 | 170 | - Unfortunately, `panic!`ing on `None` values makes this abstraction inflexible. 171 | - Better: use `expect(&self, msg: String) -> T` instead. 172 | - `panic!`s with a custom error message if a `None` value is found. 173 | 174 | --- 175 | ### `Option::map()` 176 | 177 | - Let's make the pattern a little better. 178 | - We'll take an `Option`, change the value if it exists, and return an `Option`. 179 | - Instead of failing on `None`, we'll keep it as `None`. 180 | 181 | ```rust 182 | fn map(self, f: F) -> Option 183 | where F: FnOnce(T) -> U { 184 | match self { 185 | None => None, 186 | Some(x) => Some(f(x)) 187 | } 188 | } 189 | 190 | // fn foo() -> Option 191 | 192 | let x = foo().map(|x| bar(x)); 193 | ``` 194 | 195 | --- 196 | ### `Option::and_then()` 197 | 198 | - There's a similar function `and_then`: 199 | 200 | ```rust 201 | fn and_then(self, f: F) -> Option 202 | where F: FnOnce(T) -> Option { 203 | match self { 204 | Some(x) => f(x), 205 | None => None, 206 | } 207 | } 208 | 209 | // fn foo() -> Option 210 | 211 | let x = foo().and_then(|x| Some(bar(x))); 212 | ``` 213 | 214 | - Notice the type of `f` changes from `T -> U` to `T -> Some(U)`. 215 | 216 | --- 217 | ### `Option::unwrap_or()` 218 | 219 | - If we don't want to operate on an `Option` value, but it has a sensible 220 | default value, there's `unwrap_or`. 221 | 222 | ```rust 223 | impl Option { 224 | fn unwrap_or(&self, default: T) -> T { 225 | match *self { 226 | None => default, 227 | Some(value) => value, 228 | } 229 | } 230 | } 231 | ``` 232 | 233 | --- 234 | ### `Option::unwrap_or_else()` 235 | 236 | - If you don't have a static default value, but you can write a closure to 237 | compute one: 238 | 239 | ```rust 240 | impl Option { 241 | fn unwrap_or_else(&self, f: F) -> T 242 | where F: FnOnce() -> T { 243 | match *self { 244 | None => f(), 245 | Some(value) => value, 246 | } 247 | } 248 | } 249 | ``` 250 | 251 | --- 252 | ### Other 253 | 254 | - Some other methods provided by Option: 255 | - `fn is_some(&self) -> bool` 256 | - `fn is_none(&self) -> bool` 257 | - `fn map_or(self, default: U, f: F) -> U` 258 | - `where F: FnOnce(T) -> U` 259 | - A default value`: U`. 260 | - `fn map_or_else(self, default: D, f: F) -> U` 261 | - `where D: FnOnce() -> U, F: FnOnce(T) -> U` 262 | - A default-generating closure`: D`. 263 | 264 | --- 265 | ### Other 266 | 267 | - `fn ok_or(self, err: E) -> Result` 268 | - `fn ok_or_else(self, default: F) -> Result` 269 | - `where F: FnOnce() -> E` 270 | - Similar to `unwrap_or` but returns a `Result` with a default `Err` or closure. 271 | - `fn and(self, optb: Option) -> Option` 272 | - Returns `None` if `self` is `None`, else `optb` 273 | - `fn or(self, optb: Option) -> Option` 274 | - returns `self` if `self` is `Some(_)`, else `optb` 275 | 276 | --- 277 | ## Result 278 | 279 | ```rust 280 | enum Result { 281 | Ok(T), 282 | Err(E) 283 | } 284 | ``` 285 | 286 | - `Result` is like `Option`, but it also encodes an `Err` type. 287 | - Also defines `unwrap()` and `expect()` methods. 288 | - Can be converted to an `Option` using `ok()` or `err()`. 289 | - Takes either `Ok` or `Err` and discards the other as `None`. 290 | - Can be operated on in almost all the same ways as `Option` 291 | - `and`, `or`, `unwrap`, etc. 292 | 293 | --- 294 | ## Result 295 | 296 | - Unlike `Option`, a `Result` should _always_ be consumed. 297 | - If a function returns a `Result`, you should be sure to `unwrap`/`expect` 298 | it, or otherwise handle the `Ok`/`Err` in a meaningful way. 299 | - The compiler warns you if you don't. 300 | - Not using a result could result (ha) in your program unintentionally 301 | crashing! 302 | 303 | --- 304 | ### Custom Result Aliases 305 | 306 | - A common pattern is to define a type alias for Result which uses your libary's 307 | custom Error type. 308 | 309 | ```rust 310 | use std::io::Error; 311 | 312 | type Result = Result; 313 | ``` 314 | 315 | - Typically a convenience alias; other than fixing `E = Error`, this is 316 | identical to `std::Result`. 317 | - Users of this type should namespace it: 318 | 319 | ```rust 320 | use std::io; 321 | 322 | fn foo() -> io::Result { 323 | // ... 324 | } 325 | ``` 326 | 327 | --- 328 | ## Result - `try!` 329 | 330 | - `try!` is a macro, which means it generates Rust's code at compile-time. 331 | - This means it can actually expand to pattern matching syntax patterns. 332 | - The code that `try!` generates looks roughly like this: 333 | 334 | ```rust 335 | macro_rules! try { 336 | ($e:expr) => (match $e { 337 | Ok(val) => val, 338 | Err(err) => return Err(err), 339 | }); 340 | } 341 | ``` 342 | 343 | --- 344 | ## `try!` 345 | 346 | - `try!` is a concise way to implement early returns when encountering errors. 347 | 348 | ```rust 349 | let socket1: TcpStream = try!(TcpStream::connect("127.0.0.1:8000")); 350 | 351 | // Is equivalent to... 352 | let maybe_socket: Result = 353 | TcpStream::connect("127.0.0.1:8000"); 354 | let socket2: TcpStream = 355 | match maybe_socket { 356 | Ok(val) => val, 357 | Err(err) => { return Err(err) } 358 | }; 359 | ``` 360 | 361 | - This is actually a _slight_ simplification. 362 | - Actual `try!` has some associated trait ~~magic~~logic. 363 | 364 | --- 365 | ## [Collections](https://doc.rust-lang.org/stable/std/collections/) 366 | 367 | 368 | 369 | --- 370 | ## `Vec` 371 | 372 | - Nothing new here. 373 | 374 | --- 375 | ## `VecDeque` 376 | 377 | - An efficient double-ended `Vec`. 378 | - Implemented as a ring buffer. 379 | 380 | --- 381 | ## `LinkedList` 382 | 383 | - A doubly-linked list. 384 | - Even if you want this, you probably don't want this. 385 | - Seriously, did you even read *any* of Gankro's book? 386 | 387 | --- 388 | ## `HashMap`/`BTreeMap` 389 | 390 | - Map/dictionary types. 391 | - `HashMap` is useful when you want a basic map. 392 | - Requires that `K: Hash + Eq`. 393 | - Uses "linear probing with Robin Hood bucket stealing". 394 | - `BTreeMap` is a sorted map (with slightly worse performance). 395 | - Requires that `K: Ord`. 396 | - Uses a B-tree under the hood (surprise surprise). 397 | 398 | --- 399 | ## `HashSet`/`BTreeSet` 400 | 401 | - Sets for storing unique values. 402 | - `HashSet` and `BTreeSet` are literally struct wrappers for `HashMap` and `BTreeMap`. 403 | - Same tradeoffs and requirements as their Map variants. 404 | 405 | --- 406 | ## `BinaryHeap` 407 | 408 | - A priority queue implemented with a binary max-heap. 409 | 410 | --- 411 | ## Aside: [Rust Nursery](https://github.com/rust-lang-nursery) 412 | 413 | - Useful "stdlib-ish" crates that are community-developed, but not 414 | official-official. 415 | - Contains things like: 416 | - Bindings to `libc` 417 | - A `rand` library 418 | - Regex support 419 | - Serialization 420 | - UUID generation 421 | 422 | --- 423 | ## Iterators 424 | 425 | - You've seen these in HW3! 426 | 427 | ```rust 428 | pub trait Iterator { 429 | type Item; 430 | fn next(&mut self) -> Option; 431 | 432 | // More fields omitted 433 | } 434 | ``` 435 | 436 | - A Trait with an associated type, `Item`, and a method `next` which yields that 437 | type. 438 | - Other methods (consumers and adapters) are implemented on `Iterator` as 439 | default methods using `next`. 440 | 441 | --- 442 | ## Iterators 443 | 444 | - Like everything else, there are three types of iteration: 445 | - `into_iter()`, yielding `T`s. 446 | - `iter()`, yielding `&T`s. 447 | - `iter_mut()`, yielding `&mut T`s. 448 | - A collection may provide some or all of these. 449 | 450 | --- 451 | ## Iterators 452 | 453 | - Iterators provide syntactic sugar for for loops: 454 | 455 | ```rust 456 | let values = vec![1, 2, 3, 4, 5]; 457 | { 458 | let result = match values.into_iter() { 459 | mut iter => loop { 460 | match iter.next() { 461 | Some(x) => { /* loop body */ }, 462 | None => break, 463 | } 464 | }, 465 | }; 466 | result 467 | } 468 | ``` 469 | 470 | - `into_iter()` is provided by the trait `IntoIterator`. 471 | - Automatically implemented by anything with the Trait `Iterator`. 472 | 473 | --- 474 | ## `IntoIterator` 475 | 476 | ```rust 477 | pub trait IntoIterator where Self::IntoIter::Item == Self::Item { 478 | type Item; 479 | type IntoIter: Iterator; 480 | 481 | fn into_iter(self) -> Self::IntoIter; 482 | } 483 | ``` 484 | 485 | - As you did in HW3, you can implement `IntoIterator` on a `&T` to iterate over 486 | a collection by reference. 487 | - Or on `&mut T` to iterate by mutable reference. 488 | - This allows this syntax: 489 | 490 | ```rust 491 | let ones = vec![1, 1, 1, 1, 1, 1]; 492 | 493 | for one in &ones { 494 | // Doesn't move any values. 495 | // Also, why are you doing this? 496 | } 497 | ``` 498 | 499 | --- 500 | ## Iterator Consumers 501 | 502 | - Consumers operate on an iterator and return one or more values. 503 | - There are like a billion of these, so let's look at a few. 504 | 505 | 506 | 507 | ###### Photo credit: [Hal Hefner](http://halhefner.com/) 508 | 509 | --- 510 | ## Preface: Type Transformations 511 | 512 | - Many iterator manipulators take an `Iterator` and return some other type. 513 | - e.g. `map` returns a `Map`, `filter` returns a `Filter`. 514 | - These types are just structs which themselves implement `Iterator`. 515 | - Don't worry about the internal state. 516 | - The type transformations are used mostly to enforce type safety. 517 | 518 | --- 519 | ## `collect` 520 | 521 | - `collect()` rolls a (lazy) iterator back into an actual collection. 522 | - The target collection must define the `FromIterator` trait for the `Item` 523 | inside the `Iterator`. 524 | - `collect()` sometimes needs a type hint to properly compile. 525 | - The output type can be practically any collection. 526 | 527 | ```rust 528 | fn collect(self) -> B where B: FromIterator 529 | 530 | let vs = vec![1,2,3,4]; 531 | // What type is this? 532 | let set = vs.iter().collect(); 533 | // Hint to `collect` that we want a HashSet back. 534 | // Note the lack of an explicit . 535 | let set: HashSet<_> = vs.iter().collect(); 536 | // Alternate syntax! The "turbofish" ::<> 537 | let set = vs.iter().collect::>(); 538 | ``` 539 | 540 | --- 541 | ## `fold` 542 | 543 | ```rust 544 | fn fold(self, init: B, f: F) -> B 545 | where F: FnMut(B, Self::Item) -> B; 546 | 547 | let vs = vec![1,2,3,4,5]; 548 | let sum = vs.iter().fold(0, |acc, &x| acc + x); 549 | assert_eq!(sum, 15); 550 | ``` 551 | 552 | - `fold` "folds up" an iterator into a single value. 553 | - Sometimes called `reduce` or `inject` in other languages. 554 | - `fold` takes two arguments: 555 | - An initial value or "accumulator" (`acc` above) of type `B`. 556 | - A function that takes a `B` and the type inside the iterator (`Item`) and 557 | returns a `B`. 558 | - Rust doesn't do tail-recursion, so `fold` is implemented iteratively. 559 | - [See here](https://github.com/rust-lang/rust/issues/217) if you're interested why. 560 | 561 | --- 562 | ## `filter` 563 | 564 | ```rust 565 | fn filter

(self, predicate: P) -> Filter 566 | where P: FnMut(&Self::Item) -> bool; 567 | ``` 568 | 569 | - `filter` takes a predicate function `P` and removes anything that doesn't pass 570 | the predicate. 571 | - `filter` returns a `Filter`, so you need to `collect` it to get a new 572 | collection. 573 | 574 | --- 575 | ## `find` & `position` 576 | 577 | ```rust 578 | fn find

(&mut self, predicate: P) -> Option 579 | where P: FnMut(Self::Item) -> bool; 580 | 581 | fn position

(self, predicate: P) -> TakeWhile 670 | where P: FnMut(&Self::Item) -> bool; 671 | ``` 672 | 673 | - `take` creates an iterator that yields its first `n` elements. 674 | - `take_while` takes a closure as an argument, and iterates until the closure 675 | returns `false`. 676 | - Can be used on infinite ranges to produce finite enumerations: 677 | 678 | ```rust 679 | for i in (0..).take(5) { 680 | println!("{}", i); // Prints 0 1 2 3 4 681 | } 682 | ``` 683 | 684 | --- 685 | ## `cloned` 686 | 687 | ```rust 688 | fn cloned<'a, T>(self) -> Cloned 689 | where T: 'a + Clone, Self: Iterator; 690 | ``` 691 | 692 | - Creates an iterator which calls `clone` on all of its elements. 693 | - Abstracts the common pattern `vs.iter().map(|v| v.clone())`. 694 | - Useful when you have an iterator over `&T`, but need one over `T`. 695 | 696 | --- 697 | ## `drain` 698 | 699 | - Not actually an `Iterator` method, but is very similar. 700 | - Calling `drain()` on a collection removes and returns some or all elements. 701 | - e.g. `Vec::drain(&mut self, range: R)` removes and returns a range out of a vector. 702 | 703 | --- 704 | ## Iterators 705 | 706 | - There are many more `Iterator` methods we didn't cover. 707 | - Take a look at [the docs](https://doc.rust-lang.org/std/iter/trait.Iterator.html) for the rest. 708 | 709 | -------------------------------------------------------------------------------- /05/img/collector.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/05/img/collector.jpg -------------------------------------------------------------------------------- /05/img/consume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/05/img/consume.png -------------------------------------------------------------------------------- /05/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "05 - Standard Library" 4 | --- 5 | -------------------------------------------------------------------------------- /06/content.md: -------------------------------------------------------------------------------- 1 | # `std`: Pointer Types 2 | 3 | ### CIS 198 Lecture 6 4 | 5 | ###### Reference: [TRPL 5.8](https://doc.rust-lang.org/book/choosing-your-guarantees.html) 6 | 7 | --- 8 | ## `&T` and `&mut T` 9 | 10 | - Your basic, economy-class references. 11 | - Zero runtime cost; all checks are done at compile time. 12 | - Not allowed to outlive their associated lifetime. 13 | - Can introduce serious lifetime complexity if you're not careful! 14 | - Use these unless you _actually_ need something more complicated. 15 | 16 | --- 17 | ## `Box` 18 | 19 | - A `Box` is one of Rust's ways of allocating data on the heap. 20 | - A `Box` owns a `T`, so its pointer is unique - it can't be aliased (only referenced). 21 | - `Box`es are automatically freed when they go out of scope. 22 | - Almost the same as unboxed values, but dynamically allocated. 23 | - Create a `Box` with `Box::new()`. 24 | 25 | ```rust 26 | let boxed_five = Box::new(5); 27 | ``` 28 | 29 | --- 30 | ## `Box` 31 | 32 | - Pros: 33 | - Easiest way to put something on the heap. 34 | - Zero-cost abstraction for dynamic allocation. 35 | - Shares typical borrowing and move semantics. 36 | - Automatic destruction. 37 | - Cons (ish): 38 | - The `T` is strictly owned by the `Box` - only one owner. 39 | - This means the particular variable holding the box can't go away 40 | until all references are gone - sometimes this won't work out! 41 | 42 | --- 43 | ## Aside: Box Syntax & Patterns 44 | 45 | - In homework 2 & 3, you may have noticed patterns like this: 46 | 47 | ```rust 48 | let opt_box: Option> = Some(Box::new(5)); 49 | 50 | match opt_box { 51 | Some(boxed) => { 52 | let unboxed = *boxed; 53 | println!("Some {}", unboxed); 54 | } 55 | None => println!("None :("), 56 | } 57 | 58 | ``` 59 | 60 | --- 61 | ## Aside: Box Syntax & Patterns 62 | 63 | - It's currently not possible to destructure the `Box` inside the `Option`. :( 64 | - In Nightly Rust, it is, thanks to `box` syntax! 65 | 66 | ```rust 67 | #![feature(box_syntax, box_patterns)] 68 | 69 | let opt_box = Some(box 5); 70 | 71 | match opt_box { 72 | Some(box unboxed) => println!("Some {}", unboxed), 73 | None => println!("None :("), 74 | } 75 | ``` 76 | 77 | - This may change before it reaches Stable. 78 | 79 | --- 80 | ## `std::rc::Rc` 81 | 82 | - Want to share a pointer with your friends? Use an `Rc`! 83 | - A "**R**eference **C**ounted" pointer. 84 | - Keeps track of how many aliases exist for the pointer. 85 | - Call `clone()` on an `Rc` to get a reference. 86 | - Increments its reference count. 87 | - No data gets copied! 88 | - When the ref count drops to 0, the value is freed. 89 | - The `T` can only be mutated when the reference count is 1 😕. 90 | - Same as the borrowing rules - there must be only one owner. 91 | 92 | ```rust 93 | let mut shared = Rc::new(6); 94 | { 95 | println!("{:?}", Rc::get_mut(&mut shared)); // ==> Some(6) 96 | } 97 | let mut cloned = shared.clone(); // ==> Another reference to same data 98 | { 99 | println!("{:?}", Rc::get_mut(&mut shared)); // ==> None 100 | println!("{:?}", Rc::get_mut(&mut cloned)); // ==> None 101 | } 102 | ``` 103 | 104 | --- 105 | ## `std::rc::Weak` 106 | 107 | - Reference counting has weaknesses: if a cycle is created: 108 | - A has an `Rc` to B, B has an `Rc` to A - both have count = 1. 109 | - They'll never be freed! ~~Eternal imprisonment~~ a memory leak! 110 | - This can be avoided with _weak references_. 111 | - These don't increment the _strong reference_ count. 112 | - But that means they aren't always valid! 113 | - An `Rc` can be downgraded into a `Weak` using `Rc::downgrade()`. 114 | - To access it, turn it back into `Rc`: `weak.upgrade() -> Option>` 115 | - Nothing else can be done with `Weak` - upgrading prevents the value from becoming invalid mid-use. 116 | 117 | --- 118 | ## Strong Pointers vs. Weak Pointers 119 | 120 | - When do you use an `Rc` vs. a `Weak`? 121 | - Generally, you probably want a strong reference via `Rc`. 122 | - If your ownership semantics need to convey a notion of possible access to data but no 123 | ownership, you might want to use a `Weak`. 124 | - Such a structure would also need to be okay with the `Weak` coming up as 125 | `None` when upgraded. 126 | - Any structure with reference cycles may also need `Weak`, to avoid the leak. 127 | - Note: `Rc` cycles are difficult to create in Rust, because of mutability rules. 128 | 129 | --- 130 | ## `std::rc::Rc` 131 | 132 | - Pros: 133 | - Allows sharing ownership of data. 134 | - Cons: 135 | - Has a (small) runtime cost. 136 | - Holds two reference counts (strong and weak). 137 | - Must update and check reference counts dynamically. 138 | - Reference cycles can leak memory. This can only be resolved by: 139 | - Avoiding creating dangling cycles. 140 | - Garbage collection (which Rust doesn't have). 141 | 142 | --- 143 | ## Cells 144 | 145 | - A way to wrap data to allow _interior mutability_. 146 | - An _immutable_ reference allows modifying the contained value! 147 | - There are two types of cell: `Cell` and `RefCell`. 148 | 149 | ```rust 150 | struct Foo { 151 | x: Cell, 152 | y: RefCell, 153 | } 154 | ``` 155 | 156 | --- 157 | ## `std::cell::Cell` 158 | 159 | - A wrapper type providing interior mutability for `Copy` types. 160 | - `Cell`s cannot contain references. 161 | - Get values from a `Cell` with `get()`. 162 | - Update the value inside a `Cell` with `set()`. 163 | - Can't mutate the `T`, only replace it. 164 | - Generally pretty limited, but safe & cheap. 165 | 166 | ```rust 167 | let c = Cell::new(10); 168 | c.set(20); 169 | println!("{}", c.get()); // 20 170 | ``` 171 | 172 | --- 173 | ## `std::cell::Cell` 174 | 175 | - Pros: 176 | - Interior mutability. 177 | - No runtime cost! 178 | - Small allocation cost. 179 | - Cons: 180 | - Very limited - only works on `Copy` types. 181 | 182 | --- 183 | ## `std::cell::RefCell` 184 | 185 | - A wrapper type providing interior mutability for any type. 186 | - Uses dynamic borrow checking rules (performed at runtime). 187 | - This may cause a panic at runtime. 188 | - Borrow inner data via `borrow()` or `borrow_mut()`. 189 | - These may panic if the `RefCell` is already borrowed! 190 | 191 | ```rust 192 | use std::cell::RefCell; 193 | 194 | let refc = RefCell::new(vec![12]); 195 | let mut inner = refc.borrow_mut(); 196 | inner.push(24); 197 | println!("{:?}", *inner); // [12, 24] 198 | 199 | let inner2 = refc.borrow(); 200 | // ==> Panics since refc is already borrow_mut'd! 201 | ``` 202 | 203 | --- 204 | ## `std::cell::RefCell` 205 | 206 | - A common paradigm is putting a `RefCell` inside an `Rc` to allow shared mutability. 207 | - Not thread-safe! `borrow()` et al don't prevent race conditions. 208 | - There is no way (in stable Rust) to check if a borrow will panic before executing it. 209 | - `borrow_state(&self)` is an unstable way to do this. 210 | 211 | --- 212 | ## `std::cell::RefCell` 213 | 214 | - Pros: 215 | - Interior mutability for any type. 216 | - Cons: 217 | - Stores an additional borrow state variable. 218 | - Must check borrow state to dynamically allow borrows. 219 | - May panic at runtime. 220 | - Not thread-safe. 221 | 222 | --- 223 | ## `std::cell::Ref` & `RefMut` 224 | 225 | - When you invoke `borrow()` on a `RefCell`, you actually get `Ref`, not `&T`. 226 | - Similarly, `borrow_mut()` gives you a `RefMut`. 227 | - These are pretty simple wrapper over `&T`, but define some extra methods. 228 | - Sadly, all of them are unstable pending the `cell_extras` feature 😞. 229 | 230 | --- 231 | ## `*const T` & `*mut T` 232 | 233 | - C-like raw pointers: they just point... somewhere in memory. 234 | - No ownership rules. 235 | - No lifetime rules. 236 | - Zero-cost abstraction... because there is no abstraction. 237 | - Requires `unsafe` to be dereferenced. 238 | - May eat your laundry if you're not careful. 239 | - Use these if you're building a low-level structure like `Vec`, but not in 240 | typical code. 241 | - Can be useful for manually avoiding runtime costs. 242 | - We won't get to unsafe Rust for a while, but for now: 243 | - Unsafe Rust is basically C with Rust syntax. 244 | - Unsafe means having to manually maintain Rust's assumptions 245 | (borrowing, non-nullability, non-undefined memory, etc.) 246 | -------------------------------------------------------------------------------- /06/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "07 - std: Pointer Types" 4 | --- 5 | -------------------------------------------------------------------------------- /07/content.md: -------------------------------------------------------------------------------- 1 | # Misc: Syntax, Crates, `std` 2 | 3 | ### CIS 198 Lecture 7 4 | 5 | --- 6 | ## `const` 7 | 8 | ```rust 9 | const PI: f32 = 3.1419; 10 | ``` 11 | 12 | - Defines constants that live for the duration of the program. 13 | - Must annotate the type! 14 | - Constants "live" for the duration of the program. 15 | - Think of them as being inlined every time they're used. 16 | - No guarantee that multiple references to the same constant are the same. 17 | 18 | --- 19 | ## `static` 20 | 21 | ```rust 22 | static PI: f32 = 3.1419; 23 | ``` 24 | 25 | - As above: must annotate type. 26 | - Typical global variable with fixed memory address. 27 | - All references to static variables has the `'static` lifetime, because statics 28 | live as long as the program. 29 | - `unsafe` to mutate. 30 | 31 | ```rust 32 | let life_of_pi: &'static f32 = &PI; 33 | ``` 34 | 35 | - String literals are references (with lifetime `'static`) to `static str`s. 36 | 37 | --- 38 | ## `static` 39 | 40 | ```rust 41 | static mut counter: i32 = 0; 42 | ``` 43 | 44 | - You can create mutable static variables, but you can only mutate them inside 45 | `unsafe` blocks. 46 | - Rust forces you to declare when you're doing things that are... 47 | ~~morally questionable~~ potentially going to crash your program. 48 | 49 | --- 50 | # Modules & Crates 51 | 52 | --- 53 | ## Modules 54 | 55 | - We've seen these in the homework, but not talked about them. 56 | - Everything in Rust is module-scoped: if it's not pub, it's only 57 | accessible from within the same module. 58 | - Modules can be defined within one file: 59 | 60 | ```rust 61 | mod english { 62 | pub mod greetings { 63 | } 64 | pub mod farewells { 65 | } 66 | } 67 | 68 | mod japanese { 69 | pub mod greetings { 70 | } 71 | pub mod farewells { 72 | } 73 | } 74 | ``` 75 | 76 | Reference: [TRPL 4.25](http://doc.rust-lang.org/book/crates-and-modules.html) 77 | 78 | --- 79 | ## Modules 80 | 81 | ```rust 82 | mod english { 83 | pub mod greetings { /* ... */ } 84 | } 85 | ``` 86 | 87 | - Modules can be defined as files instead: 88 | - `lib.rs`: 89 | ```rust 90 | mod english; 91 | ``` 92 | - `english.rs`: 93 | ```rust 94 | pub mod greetings { /* ... */ } 95 | ``` 96 | 97 | --- 98 | ## Modules 99 | 100 | ```rust 101 | mod english { 102 | pub mod greetings { /* ... */ } 103 | } 104 | ``` 105 | 106 | - Modules can also be defined as directories: 107 | - `lib.rs`: 108 | ```rust 109 | mod english; 110 | ``` 111 | - `english/` 112 | - `mod.rs`: 113 | ```rust 114 | pub mod greetings; 115 | ``` 116 | - `greetings.rs`: 117 | ```rust 118 | /* ... */ 119 | ``` 120 | 121 | --- 122 | ## Namespacing 123 | 124 | - When accessing a member of a module, by default, namespaces 125 | are relative to the current module: 126 | 127 | ```rust 128 | mod one { 129 | mod two { pub fn foo() {} } 130 | fn bar() { 131 | two::foo() 132 | } 133 | } 134 | ``` 135 | 136 | - But it can be made absolute with a leading `::` operator: 137 | 138 | ```rust 139 | mod one { 140 | mod two { pub fn foo() {} } 141 | fn bar() { 142 | ::one::two::foo() 143 | } 144 | } 145 | ``` 146 | 147 | --- 148 | ## `use`ing Modules 149 | 150 | - `use` has the opposite rules. 151 | - `use` directives are absolute by default: 152 | 153 | ```rust 154 | use english::greetings; 155 | ``` 156 | 157 | - But can be relative to the current module: 158 | 159 | ```rust 160 | // english/mod.rs 161 | use self::greetings; 162 | use super::japanese; 163 | ``` 164 | 165 | - `pub use` can be used to re-export other items: 166 | 167 | ```rust 168 | // default_language.rs 169 | 170 | #[cfg(english)] 171 | pub use english::*; 172 | 173 | #[cfg(japanese)] 174 | pub use japanese::*; 175 | ``` 176 | 177 | --- 178 | ## Using External Crates 179 | 180 | - For external crates, use `extern crate` instead of `mod`. 181 | 182 | ```rust 183 | extern crate rand; 184 | 185 | use rand::Rng; 186 | ``` 187 | 188 | --- 189 | ## Making Your Own Crate 190 | 191 | - We've been writing lib crates - but how do we export from them? 192 | - Anything marked `pub` in the root module (`lib.rs`) is exported: 193 | 194 | ```rust 195 | pub mod english; 196 | ``` 197 | 198 | - Easy! 199 | 200 | --- 201 | ## Using Your Own Crate 202 | 203 | - Now, you can use your own crate from Cargo: 204 | 205 | ```toml 206 | [dependencies] 207 | myfoo = { git = "https://github.com/me/foo-rs" } 208 | mybar = { path = "../rust-bar" } 209 | ``` 210 | 211 | - Or: 212 | 213 | ```toml 214 | [dependencies.myfoo] 215 | git = "https://github.com/me/foo-rs" 216 | ``` 217 | 218 | - And use them: 219 | 220 | ```rust 221 | extern crate myfoo; 222 | 223 | use myfoo::english; 224 | ``` 225 | 226 | --- 227 | ## Cargo: you got your bins in my lib 228 | 229 | - We've seen both lib and bin (executable) crates in homework 230 | - Executable-only crates don't export any importable crates. 231 | - But this isn't _really_ a distinction! 232 | - Cargo allows _both_ `:/src/lib.rs` and `:/src/main.rs`. 233 | - Cargo will also build `:/src/bin/*.rs` as executables. 234 | - Examples go in `:/examples/*.rs`. 235 | - Built by `cargo test` (to ensure examples always build). 236 | - Can be called with `cargo run --example foo`. 237 | - Integration (non-unit) tests go in `:/tests/*.rs`. 238 | - Benchmarks go in `:/benches/*.rs`. 239 | 240 | --- 241 | ## Cargo: Features 242 | 243 | - Features of a crate can be toggled at build time: 244 | - `cargo build --features using-html9` 245 | 246 | ```toml 247 | [package] 248 | name = "myfacebumblr" 249 | 250 | [features] 251 | # Enable default dependencies: require web-vortal *feature* 252 | default = ["web-vortal"] 253 | 254 | # Extra feature; now we can use #[cfg(feature = "web-vortal")] 255 | web-vortal = [] 256 | 257 | # Also require h9rbs-js *crate* with its commodore64 feature. 258 | using-html9 = ["h9rbs-js/commodore64"] 259 | 260 | [dependencies] 261 | # Optional dependency can be enabled by either: 262 | # (a) feature dependencies or (b) extern crate h9rbs_js. 263 | h9rbs-js = { optional = "true" } 264 | ``` 265 | 266 | --- 267 | ## Cargo: Build Scripts 268 | 269 | - Sometimes, you need more than what Cargo can provide. 270 | - For this, we have build scripts! 271 | - Of course, they're written in Rust. 272 | 273 | ```toml 274 | [package] 275 | build = "build.rs" 276 | ``` 277 | 278 | - Now, `cargo build` will compile and run `:/build.rs` first. 279 | 280 | --- 281 | ## Cargo: The Rabbit Hole 282 | 283 | - Cargo has a lot of features. If you're interested, check them out 284 | in the [Cargo manifest format][] documentation. 285 | 286 | [Cargo manifest format]: http://doc.crates.io/manifest.html 287 | 288 | 289 | --- 290 | # Attributes 291 | 292 | - Ways to pass information to the compiler. 293 | - `#[test]` is an attribute that annotates a function as a test. 294 | - `#[test]` annotates the next block; `#![test]` annotates the surrounding block. 295 | 296 | ```rust 297 | #[test] 298 | fn midterm1() { 299 | // ... 300 | } 301 | 302 | fn midterm2() { 303 | #![test] 304 | // ... 305 | } 306 | ``` 307 | 308 | --- 309 | ## Attributes 310 | 311 | - Use attributes to... 312 | - `#![no_std]` disable the standard library. 313 | - `#[derive(Debug)]` auto-derive traits. 314 | - `#[inline(always)]` give compiler behavior hints. 315 | - `#[allow(missing_docs)]` disable compiler warnings for certain lints. 316 | - `#![crate_type = "lib"]` provide crate metadata. 317 | - `#![feature(box_syntax)]` enable unstable syntax. 318 | - `#[cfg(target_os = "linux")]` define conditional compilation. 319 | - And [many more][reference/attributes]! 320 | 321 | [reference/attributes]: https://doc.rust-lang.org/stable/reference.html#attributes 322 | 323 | --- 324 | # Rust Code Style 325 | 326 | --- 327 | ## Rust Code Style 328 | 329 | - A [style guide][] is being _drafted_ as part of the Rust docs. 330 | - The main reason for many of the rules is to prevent pointless 331 | arguments about things like spaces and braces. 332 | - If you contribute to an open-source Rust project, it will probably be 333 | expected that you follow these rules. 334 | - The [rustfmt][] project is an automatic code formatter. 335 | 336 | [style guide]: https://github.com/rust-lang/rust/tree/master/src/doc/style 337 | [rustfmt]: https://github.com/rust-lang-nursery/rustfmt 338 | 339 | --- 340 | ## Spaces 341 | 342 | - Lines must not exceed 99 characters. 343 | - Use 4 spaces for indentation, not tabs. 344 | - No trailing whitespace at the end of lines or files. 345 | - Use spaces around binary operators: `x + y`. 346 | - Put spaces after, but not before, commas and colons: `x: i32`. 347 | - When line-wrapping function parameters, they should align. 348 | ```rust 349 | fn frobnicate(a: Bar, b: Bar, 350 | c: Bar, d: Bar) 351 | -> Bar { 352 | } 353 | ``` 354 | 355 | --- 356 | ## Braces 357 | 358 | - Opening braces always go on the same line. 359 | - Match arms get braces, except for single-line expressions. 360 | - `return` statements get semicolons. 361 | - Trailing commas (in structs, matches, etc.) should be included if the 362 | closing delimiter is on a separate line. 363 | 364 | --- 365 | ## Capitalization & Naming 366 | 367 | - You may have seen built-in lints on how to spell identifiers. 368 | - `CamelCase`: types, traits. 369 | - `lowerCamelCase`: not used. 370 | - `snake_case`: crates, modules, functions, methods, variables. 371 | - `SCREAMING_SNAKE_CASE`: static variables and constants. 372 | - `T` (single capital letter): type parameters. 373 | - `'a` (tick + short lowercase name): lifetime parameters. 374 | - Constructors and conversions should be worded: 375 | - `new`, `new_with_stuff`: constructors. 376 | - `from_foo`: conversion constructors. 377 | - `as_foo`: free non-consuming conversion. 378 | - `to_foo`: expensive non-consuming conversion. 379 | - `into_foo`: consuming conversion. 380 | 381 | --- 382 | ## Advanced `format!`ing 383 | 384 | - The `?` means debug-print. But what goes before the `:` part? 385 | - A _positional parameter_! An index into the argument list. 386 | 387 | ```rust 388 | println!("{2} {} {} {0} {} {}", 0, 1, 2, 3) // ==> "2 0 1 0 2 3" 389 | ``` 390 | 391 | - Among the specifiers with no positional parameter, they implicitly 392 | count up: `{0} {1} {2} ...`. 393 | 394 | - There are also _named parameters_: 395 | 396 | ```rust 397 | format!("{name} {}", 1, name = 2); // ==> "2 1" 398 | ``` 399 | 400 | --- 401 | ## `format!` Specifiers 402 | 403 | - We've been printing stuff out with `println!("{:?}", bst);` 404 | - There are more format specifiers than just `{}` and `{:?}`. 405 | - These all call traits in `std::fmt`: 406 | 407 | | Spec. | Trait | Spec. | Trait | Spec. | Trait | 408 | | ------ | -------- | ------ | -------- | ------ | -------- | 409 | | `{}` | Display | `{:?}` | Debug | `{:o}` | Octal | 410 | | `{:x}` | LowerHex | `{:X}` | UpperHex | `{:p}` | Pointer | 411 | | `{:b}` | Binary | `{:e}` | LowerExp | `{:E}` | UpperExp | 412 | 413 | --- 414 | ## `format!` Specifiers 415 | 416 | - There are tons of options for each of these format specifiers. 417 | - Examples: 418 | - `{:04}` -> `0010`: padding 419 | - `'{:^4}'` -> `' 10 '`: alignment (centering) 420 | - `#` indicates an "alternate" print format: 421 | - `{:#X}` -> `0xA`: including `0x` 422 | - `{:#?}`: Pretty-prints objects: 423 | 424 | ``` 425 | A { 426 | x: 5, 427 | b: B { 428 | y: 4 429 | } 430 | } 431 | ``` 432 | 433 | - Complete reference: [std::fmt](https://doc.rust-lang.org/std/fmt/) 434 | 435 | --- 436 | ## Operators 437 | 438 | - Operators are evaluated left-to-right, in the following order: 439 | - Unary operators: `!` `-` `*` `&` `&mut` 440 | - `as` casting 441 | - `*` `/` `%` multiplicative arithmetic 442 | - `+` `-` additive arithmetic 443 | - `<<` `>>` shift arithmetic 444 | - `&` bitwise and 445 | - `^` bitwise xor 446 | - `|` bitwise or 447 | - `==` `!=` `<` `>` `<=` `>=` logical comparison 448 | - `&&` logical and 449 | - `||` logical or 450 | - `=` `..` assignment and ranges 451 | - Also: `call()`, `index[]` 452 | 453 | --- 454 | ## Operator Overloading 455 | 456 | - Okay, same old, same old. We can customize these! 457 | - Rust defines these - surprise! - using traits, in `std::ops`. 458 | - `Neg`, `Not`, `Deref`, `DerefMut` 459 | - `Mul`, `Div`, `Mod` 460 | - `Add`, `Sub` 461 | - `Shl`, `Shr` 462 | - `BitAnd` 463 | - `BitXor` 464 | - `BitOr` 465 | - `Eq`, `PartialEq`, `Ord`, `PartialOrd` 466 | - `And` 467 | - `Or` 468 | - Also: `Fn`, `FnMut`, `FnOnce`, `Index`, `IndexMut`, `Drop` 469 | 470 | --- 471 | ### `From` One Type `Into` Another 472 | 473 | - Casting (`as`) cannot be overloaded - instead, we use `From` and `Into`. 474 | - `trait From { fn from(T) -> Self; }`, called like `Y::from(x)`. 475 | - `trait Into { fn into(self) -> T; }`, called like `x.into()`. 476 | - If you implement `From`, `Into` will be automatically implemented. 477 | - So you should prefer implementing `From`. 478 | 479 | ```rust 480 | struct A(Vec); 481 | impl From> for A { 482 | fn from(v: Vec) -> Self { 483 | A(v) 484 | } 485 | } 486 | ``` 487 | 488 | --- 489 | ### `From` One Type `Into` Another 490 | 491 | - But sometimes, for various reasons, implementing `From` isn't possible - only `Into`. 492 | 493 | ```rust 494 | struct A(Vec); 495 | 496 | impl From for Vec { // error: private type A in 497 | fn from(a: A) -> Self { // exported type signature. 498 | let A(v) = a; v // (This impl is exported because 499 | } // both the trait (From) and the type 500 | } // (Vec) are visible from outside.) 501 | 502 | impl Into> for A { 503 | fn into(self) -> Vec { 504 | let A(v) = self; v 505 | } 506 | } 507 | ``` 508 | 509 | --- 510 | ### Making References 511 | 512 | - `Borrow`/`BorrowMut`: "a trait for borrowing data."¹ 513 | 514 | ```rust 515 | trait Borrow { fn borrow(&self) -> &Borrowed; } 516 | ``` 517 | 518 | - `AsRef`/`AsMut`: "a cheap, reference-to-reference conversion."² 519 | 520 | ```rust 521 | trait AsRef { fn as_ref(&self) -> &T; } 522 | ``` 523 | 524 | - So... they're exactly the same? 525 | 526 | ¹ [Trait std::borrow::Borrow](https://doc.rust-lang.org/std/borrow/trait.Borrow.html) 527 | 528 | ² [Trait std::convert::AsRef](https://doc.rust-lang.org/std/convert/trait.AsRef.html) 529 | 530 | --- 531 | ### Making References 532 | 533 | - No! While the have the same definition, `Borrow` carries additional connotations: 534 | - "If you are implementing Borrow and both Self and Borrowed implement Hash, Eq, and/or Ord, they must produce the same result."¹ ² 535 | - Borrow has a blanket implementation: 536 | - `impl Borrow for T`: you can always convert `T` to `&T`. 537 | - `AsRef` actually has its own blanket implementation: 538 | - `impl<'a, T, U> AsRef for &'a T where T: AsRef` 539 | - For all `T`, if `T` implements `AsRef`, `&T` also implements `AsRef`. 540 | - All this means you usually want to implement `AsRef`. 541 | 542 | ¹ [Trait std::borrow::Borrow](https://doc.rust-lang.org/std/borrow/trait.Borrow.html) 543 | 544 | ² [aturon on Borrow vs AsMut](https://github.com/rust-lang/rust/issues/24140#issuecomment-90626264) 545 | -------------------------------------------------------------------------------- /07/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "06 - Misc: Syntax, Crates, std" 4 | --- 5 | -------------------------------------------------------------------------------- /08/content.md: -------------------------------------------------------------------------------- 1 | # I/O & Serialization 2 | 3 | ### CIS 198 Lecture 8 4 | 5 | --- 6 | # I/O 7 | 8 | --- 9 | ## Traits! 10 | 11 | ```rust 12 | pub trait Read { 13 | fn read(&mut self, buf: &mut [u8]) -> Result; 14 | 15 | // Other methods implemented in terms of read(). 16 | } 17 | 18 | pub trait Write { 19 | fn write(&mut self, buf: &[u8]) -> Result; 20 | fn flush(&mut self) -> Result<()>; 21 | 22 | // Other methods implemented in terms of write() and flush(). 23 | } 24 | ``` 25 | 26 | - Standard IO traits implemented for a variety of types: 27 | - `File`s, `TcpStream`s, `Vec`s, `&[u8]`s. 28 | - Careful: return types are `std::io::Result`, not `std::Result`! 29 | - `type Result = Result;` 30 | 31 | --- 32 | ## `std::io::Read` 33 | 34 | ```rust 35 | use std::io; 36 | use std::io::prelude::*; 37 | use std::fs::File; 38 | 39 | let mut f = try!(File::open("foo.txt")); 40 | let mut buffer = [0; 10]; 41 | 42 | // read up to 10 bytes 43 | try!(f.read(&mut buffer)); 44 | ``` 45 | 46 | - `buffer` is an array, so the max length to read is encoded into the type. 47 | - `read` returns the number of bytes read, or an `Err` specifying the problem. 48 | - A return value of `Ok(n)` guarantees that `n <= buf.len()`. 49 | - It can be `0`, if the reader is empty. 50 | 51 | --- 52 | ## Ways of Reading 53 | 54 | ```rust 55 | /// Required. 56 | fn read(&mut self, buf: &mut [u8]) -> Result; 57 | 58 | /// Reads to end of the Read object. 59 | fn read_to_end(&mut self, buf: &mut Vec) -> Result 60 | 61 | /// Reads to end of the Read object into a String. 62 | fn read_to_string(&mut self, buf: &mut String) -> Result 63 | 64 | /// Reads exactly the length of the buffer, or throws an error. 65 | fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> 66 | ``` 67 | 68 | - `Read` provides a few different ways to read into a variety of buffers. 69 | - Default implementations are provided for them using `read`. 70 | - Notice the different type signatures. 71 | 72 | --- 73 | ## Reading Iterators 74 | 75 | ```rust 76 | fn bytes(self) -> Bytes where Self: Sized 77 | 78 | // Unstable! 79 | fn chars(self) -> Bytes where Self: Sized 80 | ``` 81 | 82 | - `bytes` transforms some `Read` into an iterator which yields byte-by-byte. 83 | - The associated `Item` is `Result`. 84 | - So the type returned from calling `next()` on the iterator is 85 | `Option>`. 86 | - Hitting an `EOF` corresponds to `None`. 87 | 88 | - `chars` does the same, and will try to interpret the reader's contents as a 89 | UTF-8 character sequence. 90 | - Unstable; Rust team is not currently sure what the semantics of this 91 | should be. See issue [#27802][]. 92 | 93 | [#27802]: https://github.com/rust-lang/rust/issues/27802 94 | 95 | --- 96 | ## Iterator Adaptors 97 | 98 | ```rust 99 | fn chain(self, next: R) -> Chain 100 | where Self: Sized 101 | ``` 102 | - `chain` takes a second reader as input, and returns an iterator over all bytes 103 | from `self`, then `next`. 104 | 105 | ```rust 106 | fn take(self, limit: u64) -> Take 107 | where Self: Sized 108 | ``` 109 | - `take` creates an iterator which is limited to the first `limit` bytes of the 110 | reader. 111 | 112 | --- 113 | ## `std::io::Write` 114 | 115 | ```rust 116 | pub trait Write { 117 | fn write(&mut self, buf: &[u8]) -> Result; 118 | fn flush(&mut self) -> Result<()>; 119 | 120 | // Other methods omitted. 121 | } 122 | ``` 123 | 124 | - `Write` is a trait with two required methods, `write()` and `flush()` 125 | - Like `Read`, it provides other default methods implemented in terms of 126 | these. 127 | - `write` (attempts to) write to the buffer and returns the number of bytes 128 | written (or queued). 129 | - `flush` ensures that all written data has been pushed to the target. 130 | - Writes may be queued up, for optimization. 131 | - Returns `Err` if not all queued bytes can be written successfully. 132 | --- 133 | ## Writing 134 | 135 | ```rust 136 | let mut buffer = try!(File::create("foo.txt")); 137 | 138 | try!(buffer.write("Hello, Ferris!")); 139 | ``` 140 | 141 | --- 142 | ## Writing Methods 143 | 144 | ```rust 145 | /// Attempts to write entire buffer into self. 146 | fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... } 147 | 148 | /// Writes a formatted string into self. 149 | /// Don't call this directly, use `write!` instead. 150 | fn write_fmt(&mut self, fmt: Arguments) -> Result<()> { ... } 151 | 152 | /// Borrows self by mutable reference. 153 | fn by_ref(&mut self) -> &mut Self where Self: Sized { ... } 154 | ``` 155 | 156 | --- 157 | ## `write!` 158 | 159 | - Actually using writers can be kind of clumsy when you're doing a general 160 | application. 161 | - Especially if you need to format your output. 162 | - The `write!` macro provides string formatting by abstracting over 163 | `write_fmt`. 164 | - Returns a `Result`. 165 | 166 | ```rust 167 | let mut buf = try!(File::create("foo.txt")); 168 | 169 | write!(buf, "Hello {}!", "Ferris").unwrap(); 170 | ``` 171 | 172 | --- 173 | ## IO Buffering 174 | 175 | - IO operations are really slow. 176 | - Like, _really_ slow: 177 | 178 | ```rust 179 | TODO: demonstrate how slow IO is. 180 | ``` 181 | 182 | - Why? 183 | 184 | --- 185 | ## IO Buffering 186 | 187 | - Your running program has very few privileges. 188 | - Reads are done through the operating system (via system call). 189 | - Your program will do a _context switch_, temporarily stopping execution so 190 | the OS can gather input and relay it to your program. 191 | - This is veeeery slow. 192 | - Doing a lot of reads in rapid succession suffers hugely if you make a system 193 | call on every operation. 194 | - Solve this with buffers! 195 | - Read a huge chunk at once, store it in a buffer, then access it 196 | little-by-little as your program needs. 197 | - Exact same story with writes. 198 | 199 | --- 200 | ## BufReader 201 | 202 | ```rust 203 | fn new(inner: R) -> BufReader; 204 | ``` 205 | ```rust 206 | let mut f = try!(File::open("foo.txt")); 207 | let buffered_reader = BufReader::new(f); 208 | ``` 209 | 210 | - `BufReader` is a struct that adds buffering to *any* reader. 211 | - `BufReader` itself implements `Read`, so you can use it transparently. 212 | 213 | --- 214 | ## BufReader 215 | 216 | - `BufReader` also implements a separate interface `BufRead`. 217 | 218 | ```rust 219 | pub trait BufRead: Read { 220 | fn fill_buf(&mut self) -> Result<&[u8]>; 221 | fn consume(&mut self, amt: usize); 222 | 223 | // Other optional methods omitted. 224 | } 225 | ``` 226 | 227 | --- 228 | ## BufReader 229 | 230 | - Because `BufReader` has access to a lot of data that has not technically been 231 | read by your program, it can do more interesting things. 232 | - It defines two alternative methods of reading from your input, reading up 233 | until a certain byte has been reached. 234 | 235 | ```rust 236 | fn read_until(&mut self, byte: u8, buf: &mut Vec) 237 | -> Result { ... } 238 | fn read_line(&mut self, buf: &mut String) 239 | -> Result { ... } 240 | ``` 241 | 242 | - It also defines two iterators. 243 | 244 | ```rust 245 | fn split(self, byte: u8) 246 | -> Split where Self: Sized { ... } 247 | fn lines(self) 248 | -> Lines where Self: Sized { ... } 249 | ``` 250 | --- 251 | ## BufWriter 252 | 253 | - `BufWriter` does the same thing, wrapping around writers. 254 | 255 | ```rust 256 | let f = try!(File::create("foo.txt")); 257 | let mut writer = BufWriter::new(f); 258 | try!(buffer.write(b"Hello world")); 259 | ``` 260 | 261 | - `BufWriter` doesn't implement a second interface like `BufReader` does. 262 | - Instead, it just caches all writes until the `BufWriter` goes out of scope, 263 | then writes them all at once. 264 | 265 | --- 266 | ## `StdIn` 267 | 268 | ```rust 269 | let mut buffer = String::new(); 270 | 271 | try!(io::stdin().read_line(&mut buffer)); 272 | ``` 273 | 274 | - This is a very typical way of reading from standard input (terminal input). 275 | - `io::stdin()` returns a value of `struct StdIn`. 276 | - `stdin` implements `read_line` directly, instead of using `BufRead`. 277 | 278 | --- 279 | ## `StdInLock` 280 | 281 | - A "lock" on standard input means only that current instance of `StdIn` can 282 | read from the terminal. 283 | - So no two threads can read from standard input at the same time. 284 | - All `read` methods call `self.lock()` internally. 285 | - You can also create a `StdInLock` explicitly with the `stdin::lock()` method. 286 | 287 | ```rust 288 | let lock: io::StdInLock = io::stdin().lock(); 289 | ``` 290 | 291 | - A `StdInLock` instance implements `Read` and `BufRead`, so you can call any of 292 | the methods defined by those traits. 293 | 294 | --- 295 | ## `StdOut` 296 | 297 | - Similar to `StdIn` but interfaces with standard output instead. 298 | - Directly implements `Write`. 299 | - You don't typically use `stdout` directly. 300 | - Prefer `print!` or `println!` instead, which provide string formatting. 301 | - You can also explicitly `lock` standard out with `stdout::lock()`. 302 | 303 | --- 304 | ## Special IO Structs 305 | 306 | - `repeat(byte: u8)`: A reader which will infinitely yield the specified byte. 307 | - It will always fill the provided buffer. 308 | - `sink()`: "A writer which will move data into the void." 309 | - `empty()`: A reader which will always return `Ok(0)`. 310 | - `copy(reader: &mut R, writer: &mut W) -> Result`: copies all bytes from 311 | the reader into the writer. 312 | 313 | --- 314 | # Serialization 315 | 316 | --- 317 | ## [rustc-serialize](https://github.com/rust-lang-nursery/rustc-serialize) 318 | 319 | - Implements automatic serialization for Rust structs. 320 | - (Via compiler support.) 321 | - Usually used with JSON output: 322 | 323 | ```rust 324 | extern crate rustc_serialize; 325 | use rustc_serialize::json; 326 | 327 | #[derive(RustcDecodable, RustcEncodable)] 328 | pub struct X { a: i32, b: String } 329 | 330 | fn main() { 331 | let object = X { a: 6, b: String::from("half dozen") }; 332 | let encoded = json::encode(&object).unwrap(); 333 | // ==> the string {"a":6,"b":"half dozen"} 334 | let decoded: X = json::decode(&encoded).unwrap(); 335 | } 336 | ``` 337 | 338 | * Also has support for hex- and base64- encoded text output. 339 | 340 | --- 341 | ## [Serde](https://github.com/serde-rs/serde) 342 | 343 | - **Ser**ialization/**De**serialization. 344 | - Next generation of Rust serialization: faster, more flexible. 345 | - But API is currently in flux! We're talking about serde 0.7.0, 346 | released yesterday. (Not on crates.io as of this writing.) 347 | - Serde is easy in Rust nightly! 348 | - A compiler plugin creates attributes and auto-derived traits. 349 | - Slightly harder to use in Rust stable: 350 | - Compiler plugins aren't available. 351 | - Instead, Rust code is generated before building (via `build.rs`). 352 | - `serde_codegen` generates `.rs` files from `.rs.in` files. 353 | - And you use the `include!` macro to include the resulting files. 354 | - Separate crates for each output format: 355 | - Support for binary, JSON, MessagePack, XML, YAML. 356 | 357 | --- 358 | ## Serde 359 | 360 | - Code looks similar to `rustc_serialize`: 361 | 362 | ```rust 363 | #![feature(custom_derive, plugin)] 364 | #![plugin(serde_macros)] 365 | 366 | extern crate serde; 367 | extern crate serde_json; 368 | 369 | #[derive(Serialize, Deserialize, Debug)] 370 | pub struct X { a: i32, b: String } 371 | 372 | fn main() { 373 | let object = X { a: 6, b: String::from("half dozen") }; 374 | let encoded = serde_json::to_string(&object).unwrap(); 375 | // ==> the string {"a":6,"b":"half dozen"} 376 | let decoded: X = serde_json::from_str(&encoded).unwrap(); 377 | } 378 | ``` 379 | 380 | --- 381 | ## Serde 382 | 383 | - But there are more features! 384 | - Serializers are generated using the visitor pattern, 385 | producing code like the following. 386 | - Which can also be written manually and customized. 387 | 388 | ```rust 389 | use serde; 390 | use serde::*; 391 | use serde::ser::*; 392 | 393 | struct Point { x: i32, y: i32 } 394 | 395 | impl Serialize for Point { 396 | fn serialize(&self, sr: &mut S) 397 | -> Result<(), S::Error> where S: Serializer { 398 | sr.serialize_struct("Point", 399 | PointMapVisitor { value: self, state: 0 }) 400 | } 401 | } 402 | ``` 403 | 404 | - ... 405 | 406 | --- 407 | ## Serde 408 | 409 | ```rust 410 | struct PointMapVisitor<'a> { value: &'a Point, state: u8 } 411 | 412 | impl<'a> MapVisitor for PointMapVisitor<'a> { 413 | fn visit(&mut self, sr: &mut S) 414 | -> Result, S::Error> where S: Serializer { 415 | match self.state { 416 | 0 => { // On first call, serialize x. 417 | self.state += 1; 418 | Ok(Some(try!(sr.serialize_struct_elt("x", &self.value.x)))) 419 | } 420 | 1 => { // On second call, serialize y. 421 | self.state += 1; 422 | Ok(Some(try!(sr.serialize_struct_elt("y", &self.value.y)))) 423 | } 424 | _ => Ok(None) // Subsequently, there is no more to serialize. 425 | } 426 | } 427 | } 428 | ``` 429 | 430 | - Deserialization code is also generated - similar but messier. 431 | 432 | --- 433 | ## Serde 434 | 435 | - Custom serializers are flexible, but complicated. 436 | - Serde also provides customization via `#[serde(something)]` 437 | attributes. `something` can be: 438 | - On fields and enum variants: 439 | - `rename = "foo"`: overrides the serialized key name 440 | - On fields: 441 | - `default`: use `Default` trait to generate default values 442 | - `default = "func"` use `func()` to generate default values 443 | - `skip_serializing`: skips this field 444 | - `skip_serializing_if = "func"`: skips this field if `!func(val)` 445 | - `serialize_with = "enc"`: serialize w/ `enc(val, serializer)` 446 | - `deserialize_with = "dec"`: deserialize w/ `dec(deserializer)` 447 | - On containers (structs, enums): 448 | - `deny_unknown_fields`: error instead of ignoring unknown fields 449 | -------------------------------------------------------------------------------- /08/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "08 - IO" 4 | --- 5 | -------------------------------------------------------------------------------- /09/content.md: -------------------------------------------------------------------------------- 1 | # Networking & Web 2 | 3 | ### CIS 198 Lecture 9 4 | 5 | --- 6 | ## Networking 7 | 8 | --- 9 | ### Sockets 10 | 11 | - Most of this section is preface. 12 | - We're not actually going to cover most of what's in it directly. 13 | 14 | --- 15 | ### Sockets 16 | 17 | - A basic way to send data over the network. 18 | - Not to be confused with IPC sockets, which are a Unix thing. 19 | - Abstractly, a socket is just a channel that can send and/or receive data over 20 | some network. 21 | - Many layers of socket-programming providers: 22 | - Operating system-provided system calls. 23 | - Low-level/low-abstraction programming language standard library. 24 | - Higher-level networking libraries or libraries handling a specific 25 | protocol (e.g. HTTP). 26 | - Usually, you won't use sockets directly unless you want to do some 27 | low-level networking. 28 | - Two general types: datagram & stream. 29 | 30 | --- 31 | ### Datagram Sockets (UDP) 32 | 33 | - **U**ser **D**atagram **P**rotocol sockets 34 | - Stateless: no connection to establish with another network device. 35 | - Simply send data to a destination IP and port, and assume they're 36 | listening. 37 | - "At least once" delivery. 38 | - Packets are not guaranteed to be delivered in order. 39 | - Packets may be received more than once. 40 | - Traditionally implement two methods: 41 | - send_to(addr) -- sends data over the socket to the specified address 42 | - recv_from() -- listens for data being sent to the socket 43 | 44 | --- 45 | ### `std::net::UdpSocket` 46 | 47 | ```rust 48 | // Try to bind a UDP socket 49 | let mut socket = try!(UdpSocket::bind("127.0.0.1:34254")); 50 | 51 | // Try to receive data from the socket we've bound 52 | let mut buf = [0; 10]; 53 | let (amt, src) = try!(socket.recv_from(&mut buf)); 54 | 55 | // Send a reply to the socket we just received data from 56 | let buf = &mut buf[..amt]; 57 | buf.reverse(); 58 | try!(socket.send_to(buf, &src)); 59 | 60 | // Close the socket 61 | drop(socket); 62 | ``` 63 | 64 | ¹Taken from the Rust docs. 65 | 66 | --- 67 | ### Stream Sockets (TCP) 68 | 69 | - "This is where the drugs kick in" - Matt Blaze on TCP sockets 70 | - **T**ransmission **C**ontrol **P**rotocol sockets 71 | - Stateful: require a connection to be established and acknowledged between two 72 | clients (using SYN packet). 73 | 74 | - Connection must also be explicitly closed. 75 | - Packets are delivered in-order, exactly once. 76 | - Achieved via packet sequence numbers. 77 | - Packets have delivery acknowledgement (ACK packet). 78 | - Generally two types of TCP socket: 79 | - TCP listeners: listen for data 80 | - TCP streams: send data 81 | 82 | --- 83 | ### `std::net::TcpStream` 84 | 85 | - A TCP stream between a local socket and a remote socket. 86 | 87 | ```rust 88 | // Create a TCP connection 89 | let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); 90 | 91 | // Uses std::io::{Read, Write} 92 | 93 | // Try to write a byte to the stream 94 | let write_result = stream.write(&[1]); 95 | 96 | // Read from the stream into buf 97 | let mut buf = [0; 128]; 98 | let read_result = stream.read(&mut buf); 99 | 100 | // ... 101 | // Socket gets automatically closed when it goes out of scope 102 | ``` 103 | 104 | --- 105 | ### `std::net::TcpListener` 106 | 107 | - A TCP socket server. 108 | 109 | ```rust 110 | let listener = TcpListener::bind("127.0.0.1:80").unwrap(); 111 | 112 | fn handle_client(stream: TcpStream) { /* ... */ } 113 | 114 | // Accept connections and process them, 115 | // spawning a new thread for each one. 116 | for stream in listener.incoming() { 117 | match stream { 118 | Ok(stream) => { 119 | thread::spawn(move|| { 120 | // connection succeeded 121 | handle_client(stream) 122 | }); 123 | } 124 | Err(e) => { /* connection failed */ } 125 | } 126 | } 127 | 128 | // close the socket server 129 | drop(listener); 130 | ``` 131 | 132 | --- 133 | ### `SocketAddr` 134 | 135 | - A socket address representation. 136 | - May be either IPv4 or IPv6. 137 | - Easily created using... 138 | 139 | --- 140 | ### `ToSocketAddrs` 141 | 142 | ```rust 143 | pub trait ToSocketAddrs { 144 | type Iter: Iterator; 145 | fn to_socket_addrs(&self) -> Result; 146 | } 147 | ``` 148 | 149 | - A trait for objects which can be converted into `SocketAddr` values. 150 | - Methods like `TcpStream::connect(addr: A)` specify that `A: ToSocketAddr`. 151 | - This makes it easier to specify what may be converted to a socket 152 | address-like object. 153 | - See the docs for the full specification. 154 | 155 | --- 156 | ## [Web](http://arewewebyet.com/) 157 | 158 | 159 | 160 | --- 161 | ## [Web](http://arewewebyet.com/) 162 | 163 | 164 | 165 | --- 166 | ### HTTP - Preface 167 | 168 | - HTTP defines several common methods for interacting with servers & clients 169 | over the Internet 170 | - Common HTTP verbs are: 171 | - GET: retrieve data (e.g. access a web page) 172 | - POST: send data (e.g. submit a login form) 173 | - PATCH: modify existing data (e.g. modify a user profile) 174 | - DELETE: delete data (e.g. delete a user) 175 | - Others exist, but are less common 176 | 177 | --- 178 | ### HTTP - Preface 179 | 180 | - An HTTP _request_ is made by sending some data to a server over HTTP 181 | containing some data, such as: 182 | - the URL of the server 183 | - the method you want to invoke 184 | - data the server needs to look at (like in a POST) 185 | - names of data you want back from the server 186 | - etc. 187 | 188 | --- 189 | ### HTTP - Preface 190 | 191 | - Once the server processes your request, you get a _response_ 192 | - Responses usually contain: 193 | - a status code (200, 404, 502, etc.) 194 | - some information pertaining to your request: 195 | - HTML content 196 | - JSON-formatted data 197 | - Error messages 198 | - etc. 199 | 200 | --- 201 | ### [Hyper](http://hyper.rs) 202 | 203 | - "A Modern HTTP library for Rust" 204 | - Provides a relatively low-level wrapper over raw HTTP. 205 | - (Examples below won't run on the Rust Playpen since they require `extern 206 | crate`s) 207 | - Because you never want to implement the HTTP protocol yourself. 208 | 209 | --- 210 | ### `hyper::client` 211 | 212 | - An HTTP client. 213 | - Designed for most people to make HTTP requests, using the `client::Request` 214 | API. 215 | 216 | ```rust 217 | let client = Client::new(); 218 | 219 | // GET 220 | let res = client.get("http://cis.upenn.edu/~cis198") 221 | .send().unwrap(); 222 | assert_eq!(res.status, hyper::Ok); 223 | 224 | // POST 225 | let res = client.post("http://cis.upenn.edu/~cis198") 226 | .body("user=me") 227 | .send().unwrap(); 228 | assert_eq!(res.status, hyper::Ok); 229 | 230 | // PUT, PATCH, DELETE, etc. are all legal verbs too. 231 | ``` 232 | 233 | - `Client` is shareable between threads, so you can make requests _in parallel by default_! 234 | 235 | --- 236 | ### `Client` Requests 237 | 238 | - Let's see some full client examples with proper error handling. 239 | - A GET request that reads out the body of a web page: 240 | 241 | ```rust 242 | extern crate hyper; 243 | 244 | use std::io::Read; 245 | use hyper::client::Client; 246 | 247 | // GET 248 | fn get_contents(url: &str) -> hyper::Result { 249 | let client = Client::new(); 250 | let mut response = try!(client.get(url).send()); 251 | let mut buf = String::new(); 252 | try!(response.read_to_string(&mut buf)); 253 | Ok(buf) 254 | } 255 | 256 | println!("{}", get_contents("http://cis198-2016s.github.io/") 257 | .unwrap()); // A whole mess of HTML 258 | ``` 259 | 260 | ¹Adapted from Zbigniew Siciarz's [24 Days of Rust](http://zsiciarz.github.io/24daysofrust) 261 | 262 | --- 263 | ### `Client` Requests 264 | 265 | - A POST request, using `form_urlencoded` from the `url` crate to do URL encoding: 266 | 267 | ```rust 268 | extern crate hyper; 269 | extern crate url; 270 | use url::form_urlencoded; 271 | 272 | // POST 273 | fn post_query(url: &str, query: Vec<(&str, &str)>) 274 | -> hyper::Result { 275 | let body = form_urlencoded::serialize(query); 276 | 277 | let client = Client::new(); 278 | let mut response = try!(client.post(url).body(&body[..]).send()); 279 | let mut buf = String::new(); 280 | try!(response.read_to_string(&mut buf)); 281 | Ok(buf) 282 | } 283 | 284 | let query = vec![("user", "me"), ("email", "me@email.email")]; 285 | println!("{}", post_query("http://httpbin.org/post", query) 286 | .unwrap()); 287 | ``` 288 | 289 | --- 290 | ### `Client` Requests 291 | 292 | - Using `rustc_serialize`, we can generalize our POST request encoding to allow 293 | any data that's encodable to JSON! 294 | 295 | ```rust 296 | extern crate rustc_serialize; 297 | use rustc_serialize::{Encodable, json}; 298 | 299 | fn post_json(url: &str, payload: &T) 300 | -> hyper::Result { 301 | let body = json::encode(payload).unwrap(); 302 | 303 | // same as before from here 304 | } 305 | ``` 306 | 307 | --- 308 | ### `hyper::server` 309 | 310 | - An HTTP server that listens on a port, parses HTTP requests, and passes them 311 | to a handler. 312 | - `Server` listens to multiple threads by default. 313 | - You must define a `Handler` for `Server` to define how it handles requests. 314 | 315 | ```rust 316 | use hyper::{Server, Request, Response}; 317 | 318 | fn hello(req: Request, res: Response) { 319 | res.send(b"Hello World!").unwrap(); 320 | } 321 | 322 | Server::http("127.0.0.1:3000").unwrap().handle(hello).unwrap(); 323 | ``` 324 | 325 | --- 326 | ### `hyper::server::Handler` 327 | 328 | - `Handler` is a trait that defines how requests will be handled by the server. 329 | - It only requires one method, `handle`, which just takes a `Request` and a 330 | `Response` and does something to them. 331 | - (It actually defines 3 more methods, but they all have default 332 | implementations.) 333 | 334 | -------------------------------------------------------------------------------- /09/img/web1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/09/img/web1.png -------------------------------------------------------------------------------- /09/img/web2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/09/img/web2.png -------------------------------------------------------------------------------- /09/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "09 - Networking & Web" 4 | --- 5 | -------------------------------------------------------------------------------- /10/content.md: -------------------------------------------------------------------------------- 1 | # Concurrency I 2 | 3 | ### CIS 198 Lecture 10 4 | 5 | --- 6 | ## Misc. 7 | 8 | Rust 1.7 releasing on Thursday, 3/03! 9 | 10 | - If using multirust: `multirust update stable` 11 | - If not: just run the installer again, it will provide the option to update a 12 | toolchain rather than re-installing it. 13 | 14 | --- 15 | ## What is Concurrency? 16 | 17 | - One program with multiple threads of execution running at the same time. 18 | - Threads can share data without communication overhead. 19 | - (networking, inter-process communication channels, etc). 20 | - Threads are more lightweight than individual processes. 21 | - No large OS context switch when switching between threads. 22 | 23 | --- 24 | ## What is a Thread? 25 | 26 | - A context in which instructions are being executed. 27 | - References to some data (which may or may not be shared). 28 | - A set of register values, a stack, and some other information about the 29 | current execution context (low-level). 30 | 31 | --- 32 | ## Threads 33 | 34 | - Conceptually, every program has at least one thread. 35 | - There is a thread scheduler which manages the execution of these threads. 36 | - It can arbitrarily decide when to run any thread. 37 | - Programs can create and start new threads, which will be picked up by the 38 | scheduler. 39 | 40 | --- 41 | ## Concurrent Execution 42 | 43 | - Take these two simple programs, written in pseudo-Rust 44 | (ignoring ownership semantics): 45 | 46 | ```rust 47 | let mut x = 0; 48 | 49 | fn foo() { 50 | let mut y = &mut x; 51 | *y = 1; 52 | println!("{}", *y); // foo expects 1 53 | } 54 | 55 | fn bar() { 56 | let mut z = &mut x; 57 | *z = 2; 58 | println!("{}", *z); // bar expects 2 59 | } 60 | ``` 61 | 62 | --- 63 | ### Instruction Interleaving 64 | 65 | - Imagine two threads: one executing `foo`, one executing `bar`. 66 | - The scheduler can interleave instructions however it wants. 67 | - Thus, the above programs may be executed like this: 68 | 69 | ```rust 70 | /* foo */ let mut y = &mut x; 71 | /* foo */ *y = 1; 72 | /* foo */ println!("{}", *y); // foo expects 1 73 | // => 1 74 | /* bar */ let mut z = &mut x; 75 | /* bar */ *z = 2; 76 | /* bar */ println!("{}", *z); // bar expects 2 77 | // => 2 78 | ``` 79 | - ...and everything works as expected. 80 | 81 | --- 82 | ### Instruction Interleaving 83 | 84 | - However, there is no guarantee that execution happens in that order every time, 85 | or at all! 86 | - We need some mechanisms to ensure that events happen in an order that produces 87 | the expected results. 88 | - Otherwise, `foo` and `bar` may be interleaved arbitrarily, causing unexpected 89 | results: 90 | 91 | ```rust 92 | /* bar */ let mut z = &mut x; 93 | /* bar */ *z = 2; 94 | /* foo */ let mut y = &mut x; 95 | /* foo */ *y = 1; 96 | /* bar */ println!("{}", *z); // bar expects 2 97 | // => 1 98 | /* foo */ println!("{}", *y); // foo expects 1 99 | // => 1 100 | ``` 101 | 102 | --- 103 | ## Why is concurrency hard? 104 | 105 | - **Sharing data:** What if two threads try to write to the same piece of 106 | data at the same time? 107 | - Writing to `x` in the previous example. 108 | - **Data races:** The behavior of the same piece of code might change depending 109 | on when exactly it executes. 110 | - Reading from `x` in the previous example. 111 | 112 | --- 113 | ## Why is concurrency hard? 114 | 115 | - **Synchronization:** How can I be sure all of my threads see the correct world view? 116 | - A series of threads shares the same buffer. Each thread `i` writes to 117 | `buffer[i]`, then tries to read from the entire buffer to decide its next 118 | action. 119 | - When sending data between threads, how can you be sure the other thread 120 | receives the data at the right point in execution? 121 | - **Deadlock:** How can you safely share resources across threads and ensure 122 | threads don't lock each other out of data access? 123 | 124 | --- 125 | ## Deadlock 126 | 127 | - A deadlock occurs when multiple threads want to access some shared resources, 128 | but end up creating a state in which no one is able to access anything. 129 | - There are four preconditions for deadlock: 130 | - Mutual exclusion: One resource is locked in a non-sharable mode. 131 | - Resource holding: A thread holds a resource and asks for more resources, 132 | which are held by other threads. 133 | - No preemption: A resource can only be released voluntarily by its holder. 134 | - Circular waiting: A cycle of waiting on resources from other threads 135 | exists. 136 | - To avoid deadlock, we only have to remove _one_ precondition. 137 | 138 | --- 139 | ## Dining Philosophers 140 | 141 | - An oddly-named classical problem for illustrating deadlock. 142 | - N philosophers sit in a circle and want to alternate between eating and 143 | thinking. 144 | - Each philosopher needs two forks to eat. There are `N` forks, one between 145 | every pair of philosophers. 146 | - Algorithm: 147 | - A philosopher picks up their left fork. (Acquire a resource lock) 148 | - They pick up their right fork. (Acquire a resource lock) 149 | - They eat. (Use the resource) 150 | - They put both forks down. (Release resource locks) 151 | 152 | --- 153 | ## Dining Philosophers 154 | 155 | - What happens when we do this? 156 | - Let N = 3, for simplicity. 157 | - Philosopher 1 picks up their left fork. 158 | - Philosopher 2 picks up their left fork. 159 | - Philosopher 3 picks up their left fork. 160 | - Philosophers 1, 2, and 3 all try to pick up their right fork, but get stuck, 161 | since all forks are taken! 162 | 163 | --- 164 | ## Dining Philosophers 165 | 166 | - A better algorithm? 167 | - We'll revisit this at the end of the lecture. 168 | 169 | --- 170 | ## Rust Threads 171 | 172 | - Rust's standard library contains a threading library, `std::thread`. 173 | - Other threading models have been added and removed over time. 174 | - The Rust "runtime" was been removed. 175 | - Each thread in Rust has its own stack and local state. 176 | - In Rust, you define the behavior of a thread with a closure: 177 | 178 | ```rust 179 | use std::thread; 180 | 181 | thread::spawn(|| { 182 | println!("Hello, world!"); 183 | }); 184 | ``` 185 | 186 | --- 187 | ## Thread Handlers 188 | 189 | - `thread::spawn` returns a thread handler of type `JoinHandler`. 190 | 191 | ```rust 192 | use std::thread; 193 | 194 | let handle = thread::spawn(|| { 195 | "Hello, world!" 196 | }); 197 | 198 | println!("{:?}", handle.join().unwrap()); 199 | // => Ok("Hello, world!") 200 | ``` 201 | - `join()` will block until the thread has terminated. 202 | - `join()` returns an `Ok` of the thread's final expression (or return 203 | value), or an `Err` of the thread's `panic!` value. 204 | 205 | --- 206 | ## `std::thread::JoinHandler` 207 | 208 | - A thread is detached when its handler is dropped. 209 | - Cannot be cloned; only one variable has the permission to join a thread. 210 | 211 | --- 212 | ## `panic!` 213 | 214 | - Thread panic is unrecoverable from _within_ the panicking thread. 215 | - Rust threads `panic!` independently of the thread that created them. 216 | - _Only_ the thread that panics will crash. 217 | - The thread will unwind its stack, cleaning up resources. 218 | - The message passed to `panic!` can be read from other threads. 219 | - If the main thread panics or otherwise ends, all other threads will be shut down. 220 | - The main thread can choose to wait for all threads to finish before finishing itself. 221 | 222 | ??? 223 | 224 | Freeing allocations, running destructors. 225 | 226 | --- 227 | ## `std::thread::Thread` 228 | 229 | - The currently running thread can stop itself with `thread::park()`. 230 | - `Thread`s can be unparked with `.unpark()`. 231 | 232 | ```rust 233 | use std::thread; 234 | 235 | let handle = thread::spawn(|| { 236 | thread::park(); 237 | println!("Good morning!"); 238 | }); 239 | println!("Good night!"); 240 | handle.thread().unpark(); 241 | ``` 242 | 243 | - A `JoinHandler` provides `.thread()` to get that thread's `Thread`. 244 | - You can access the currently running `Thread` with `thread::current()`. 245 | 246 | --- 247 | ## Many Threads 248 | 249 | - You can create many threads at once: 250 | 251 | ```rust 252 | use std::thread; 253 | 254 | for i in 0..10 { 255 | thread::spawn(|| { 256 | println!("I'm first!"); 257 | }); 258 | } 259 | ``` 260 | 261 | --- 262 | ## Many Threads 263 | 264 | - Passing ownership of a variable into a thread works just like the rest of the 265 | ownership model: 266 | 267 | ```rust 268 | use std::thread; 269 | 270 | for i in 0..10 { 271 | thread::spawn(|| { 272 | println!("I'm #{}!", i); 273 | }); 274 | } 275 | // Error! 276 | // closure may outlive the current function, but it borrows `i`, 277 | // which is owned by the current function 278 | ``` 279 | - ...including having to obey closure laws. 280 | 281 | --- 282 | ## Many Threads 283 | 284 | - The closure needs to own `i`. 285 | - Fix: Use `move` to make a movable closure that takes ownership of its scope. 286 | 287 | ```rust 288 | use std::thread; 289 | 290 | for i in 0..10 { 291 | thread::spawn(move || { 292 | println!("I'm #{}!", i); 293 | }); 294 | } 295 | ``` 296 | 297 | ??? 298 | 299 | Same thing as when you return a closure from a function. 300 | 301 | --- 302 | ## `Send` and `Sync` 303 | 304 | - Rust's type system includes traits for enforcing certain concurrency 305 | guarantees. 306 | - `Send`: a type can be safely transferred between threads. 307 | - `Sync`: a type can be safely shared (with references) between threads. 308 | - Both `Send` and `Sync` are marker traits, which don't implement any methods. 309 | 310 | --- 311 | ## `Send` 312 | 313 | ```rust 314 | pub unsafe trait Send { } 315 | ``` 316 | 317 | - A `Send` type may have its ownership tranferred across threads. 318 | - Not implementing `Send` enforces that a type _may not_ leave its original thread. 319 | - e.g. a C-like raw pointer, which might point to data aliased by another 320 | (mutable) raw pointer that could modify it in a thread-unsafe way. 321 | 322 | --- 323 | ## `Sync` 324 | 325 | ```rust 326 | pub unsafe trait Sync { } 327 | ``` 328 | 329 | - A `Sync` type cannot introduce memory unsafety when used across multiple 330 | threads (via shared references). 331 | - All primitive types are `Sync`; all aggregate types containing only items that 332 | are `Sync` are also `Sync`. 333 | - Immutable types (`&T`) and simple inherited mutability (`Box`) are 334 | `Sync`. 335 | - Actually, all types without interior mutability are inherently (and automatically) `Sync`. 336 | 337 | --- 338 | ## `Sync` 339 | 340 | - A type `T` is `Sync` if `&T` is thread-safe. 341 | - `T` is thread safe if there is no possibility of data races when passing 342 | `&T` references between threads 343 | - Consequently, `&mut T` is also `Sync` if `T` is `Sync`. 344 | - An `&mut T` stored in an aliasable reference (an `& &mut T`) becomes 345 | immutable and has no risk of data races. 346 | - Types like `Cell` are not `Sync` because they allow their contents to be 347 | mutated even when in an immutable, aliasable slot. 348 | - The contents of an `&Cell` could be mutated, even when shared across 349 | threads. 350 | 351 | --- 352 | ## Unsafety 353 | 354 | - Both `Send` and `Sync` are `unsafe` to implement, even though they have no 355 | required functionality. 356 | - Marking a trait as `unsafe` indicates that the implementation of the trait 357 | must be trusted to uphold the trait's guarantees. 358 | - The guarantees the trait makes must be assumed to hold, regardless of 359 | whether it does or not. 360 | - `Send` and `Sync` are unsafe because thread safety is not a property that can 361 | be guaranteed by Rust's safety checks. 362 | - Thread unsafety can only be 100% prevented by _not using threads_. 363 | - `Send` and `Sync` require a level of trust that safe code alone cannot provide. 364 | 365 | --- 366 | ## Derivation 367 | 368 | - `Send` is auto-derived for all types whose members are all `Sync`. 369 | - Symmetrically, `Sync` is auto-derived for all types whose members are all 370 | `Send`. 371 | - They can be trivially `impl`ed, since they require no members: 372 | 373 | ```rust 374 | unsafe impl Send for Foo {} 375 | unsafe impl Sync for Foo {} 376 | ``` 377 | 378 | --- 379 | ## Derivation 380 | 381 | - If you need to remove an automatic derivation, it's possible. 382 | - Types which _appear_ `Sync` but _aren't_ (due to `unsafe` 383 | implementation) must be marked explicitly. 384 | - Doing this requires so-called 385 | ["OIBITs": "opt-in builtin traits"](https://github.com/rust-lang/rust/issues/13231). 386 | 387 | ```rust 388 | #![feature(optin_builtin_traits)] 389 | 390 | impl !Send for Foo {} 391 | impl !Sync for Foo {} 392 | ``` 393 | 394 | > _The acronym "OIBIT", while quite fun to say, is quite the anachronism. It 395 | > stands for "opt-in builtin trait". But in fact, Send and Sync are neither 396 | > opt-in (rather, they are opt-out) nor builtin (rather, they are defined in 397 | > the standard library). It seems clear that it should be changed._ 398 | > [—nikomatsakis](https://internals.rust-lang.org/t/pre-rfc-renaming-oibits-and-changing-their-declaration-syntax/3086) 399 | 400 | --- 401 | ## Sharing Thread State 402 | 403 | - The following code looks like it works, but doesn't compile: 404 | 405 | ```rust 406 | use std::thread; 407 | use std::time::Duration; 408 | 409 | fn main() { 410 | let mut data = vec![1, 2, 3]; 411 | 412 | for i in 0..3 { 413 | thread::spawn(move || { 414 | data[i] += 1; 415 | }); 416 | } 417 | 418 | thread::sleep(Duration::from_millis(50)); 419 | } 420 | 421 | // error: capture of moved value: `data` 422 | // data[i] += 1; 423 | // ^~~~ 424 | ``` 425 | 426 | --- 427 | ## Sharing Thread State 428 | 429 | - If each thread were to take a reference to `data`, and then independently 430 | take ownership of `data`, `data` would have multiple owners! 431 | - In order to share `data`, we need some type we can share safely between threads. 432 | - In other words, we need some type that is `Sync`. 433 | 434 | --- 435 | ### `std::sync::Arc` 436 | 437 | - One solution: `Arc`, an **A**tomic **R**eference-**C**ounted pointer! 438 | - Pretty much just like an `Rc`, but is thread-safe due to atomic reference counting. 439 | - Also has a corresponding `Weak` variant. 440 | - Let's see this in action... 441 | 442 | --- 443 | ## Sharing Thread State 444 | 445 | - This looks like it works, right? 446 | 447 | ```rust 448 | use std::sync::Arc; 449 | use std::thread; 450 | use std::time::Duration; 451 | 452 | fn main() { 453 | let mut data = Arc::new(vec![1, 2, 3]); 454 | 455 | for i in 0..3 { 456 | let data = data.clone(); // Increment `data`'s ref count 457 | thread::spawn(move || { 458 | data[i] += 1; 459 | }); 460 | } 461 | 462 | thread::sleep(Duration::from_millis(50)); 463 | } 464 | ``` 465 | 466 | --- 467 | ## Sharing Thread State 468 | 469 | - Unfortunately, not quite. 470 | 471 | ``` 472 | error: cannot borrow immutable borrowed content as mutable 473 | data[i] += 1; 474 | ^~~~ 475 | ``` 476 | 477 | - Like `Rc`, `Arc` has no interior mutability. 478 | - Its contents cannot be mutated unless it has one strong reference and no weak 479 | references. 480 | - Cloning the `Arc` naturally prohibits doing this. 481 | 482 | --- 483 | ## Many Threads 484 | 485 | - `Arc` assumes its contents must be `Sync` as well, so we can't mutate 486 | anything inside the `Arc`. :( 487 | - What could we do to solve this? 488 | - We can't use a `RefCell`, since already know these aren't thread-safe. 489 | - Instead, we need to use a `Mutex`. 490 | 491 | --- 492 | ## Mutexes 493 | 494 | - Short for **Mut**ual **Ex**clusion. 495 | - Conceptually, a mutex ensures that a value can only ever be accessed by one 496 | thread at a time. 497 | - In order to access data guarded by a mutex, you need to acquire the 498 | mutex's lock. 499 | - If someone else currently has the lock, you can either give up and try again 500 | later, or block (wait) until the lock is available. 501 | 502 | --- 503 | ## `std::sync::Mutex` 504 | 505 | - When a value is wrappted in a `Mutex`, you must call `lock` on the `Mutex` to 506 | get access to the value inside. This method returns a `LockResult`. 507 | - If the mutex is locked, the method will block until the mutex becomes unlocked. 508 | - If you don't want to block, call `try_lock` instead. 509 | - When the mutex unlocks, `lock` returns a `MutexGuard`, which you can dereference 510 | to access the `T` inside. 511 | 512 | --- 513 | ## Mutex Poisoning 🐍 514 | 515 | - If a thread acquires a mutex lock and then panics, the mutex is considered 516 | _poisoned_, as the lock was never released. 517 | - This is why `lock()` returns a `LockResult`. 518 | - `Ok(MutexGuard)`: the mutex was not poisoned and may be used. 519 | - `Err(PoisonError)`: a poisoned mutex. 520 | - If you determine that a mutex has been poisoned, you may still access the 521 | underlying guard by calling `into_inner()`, `get_ref()`, or `get_mut()` on the `PoisonError`. 522 | - This may result in accessing incorrect data, depending on what the 523 | poisoning thread was doing. 524 | 525 | --- 526 | ## Sharing Thread State 527 | 528 | - Back to our example: 529 | 530 | ```rust 531 | use std::sync::Arc; 532 | use std::thread; 533 | use std::time::Duration; 534 | 535 | fn main() { 536 | let mut data = Arc::new(Mutex::new(vec![1, 2, 3])); 537 | 538 | for i in 0..3 { 539 | let data = data.clone(); // Increment `data`'s ref count 540 | thread::spawn(move || { 541 | let mut data = data.lock().unwrap(); 542 | data[i] += 1; 543 | }); 544 | } 545 | 546 | thread::sleep(Duration::from_millis(50)); 547 | } 548 | ``` 549 | 550 | --- 551 | ## Sharing Thread State 552 | 553 | - At the end of this example, we put the main thread to sleep for 50ms to wait 554 | for all threads to finish executing. 555 | - This is totally arbitrary; what if each thread takes much longer than 556 | that? 557 | - We have no way to synchronize our threads' executions, or to know when they've 558 | all finished! 559 | 560 | --- 561 | ## Channels 562 | 563 | - Channels are one way to synchronize threads. 564 | - Channels allow passing messages between threads. 565 | - Can be used to signal to other threads that some data is ready, some event has 566 | happened, etc. 567 | 568 | --- 569 | ## `std::sync::mpsc` 570 | 571 | - **M**ulti-**P**roducer, **S**ingle-**C**onsumer communication primitives. 572 | - Three main types: 573 | - `Sender` 574 | - `SyncSender` 575 | - `Receiver` 576 | - `Sender` or `SyncSender` can be used to send data to a `Receiver` 577 | - Sender types may be cloned and given to multiple threads to create *multiple producers*. 578 | - However, Receivers cannot be cloned (*single consumer*). 579 | 580 | --- 581 | ## `std::sync::mpsc` 582 | 583 | - A linked `(Sender, Receiver)` pair may be created using the 584 | `channel()` function. 585 | - `Sender` is an _asynchronous_ channel. 586 | - Sending data across the channel will never block the sending thread, since the 587 | channel is asynchronous. 588 | - `Sender` has a conceptually-infinite buffer. 589 | - Trying to receive data from the `Receiver` will block the receiving thread until data arrives. 590 | 591 | --- 592 | ## `std::sync::mpsc` 593 | 594 | ```rust 595 | use std::thread; 596 | use std::sync::mpsc::channel; 597 | 598 | fn main() { 599 | let (tx, rx) = channel(); 600 | for i in 0..10 { 601 | let tx = tx.clone(); 602 | thread::spawn(move|| { 603 | tx.send(i).unwrap(); 604 | }); 605 | } 606 | drop(tx); 607 | 608 | let mut acc = 0; 609 | while let Ok(i) = rx.recv() { 610 | acc += i; 611 | } 612 | assert_eq!(acc, 45); 613 | } 614 | ``` 615 | 616 | --- 617 | ## `std::sync::mpsc` 618 | 619 | - A linked `(SyncSender, Receiver)` pair may be created using the 620 | `sync_channel()` function. 621 | - `SyncSender` is, naturally, synchronized. 622 | - `SyncSender` _does_ block when you send a message. 623 | - `SyncSender` has a bounded buffer, and will block until there is buffer 624 | space available. 625 | - Since this `Receiver` is the same as the one we got from `channel()`, it will 626 | obviously also block when it tries to receive data. 627 | 628 | --- 629 | ## `std::sync::mpsc` 630 | 631 | - All channel send/receive operations return a `Result`, where an error 632 | indicates the other half of the channel "hung up" (was dropped). 633 | - Once a channel becomes disconnected, it cannot be reconnected. 634 | 635 | --- 636 | ## So, is concurrency still hard? 637 | 638 | - **Sharing data is hard:** Share data with `Send`, `Sync`, and `Arc` 639 | - **Data races:** `Sync` 640 | - **Synchronization:** Communication using channels. 641 | - **Deadlock:** ?? 642 | 643 | --- 644 | ## Dining Philosophers 645 | 646 | - This illustrates a problem with the concurrency primitives we've seen so far: 647 | - While they can keep our code safe, they do not guard against all possible 648 | logical bugs. 649 | - These are not "magic bullet" concepts. 650 | - Solutions? 651 | - There are many: 652 | - The final philosopher pick up their forks in the opposite order. 653 | - A token may be passed between philosophers, and a philosopher may only try 654 | to pick up forks when they have the token. 655 | -------------------------------------------------------------------------------- /10/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "10 - Concurrency I" 4 | --- 5 | -------------------------------------------------------------------------------- /11/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "11 - Concurrency II" 4 | --- 5 | -------------------------------------------------------------------------------- /12/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "12 - Unsafe Rust" 4 | --- 5 | -------------------------------------------------------------------------------- /13/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "13 - Macros!" 4 | --- 5 | -------------------------------------------------------------------------------- /14/content.md: -------------------------------------------------------------------------------- 1 | # Community & Contributing 2 | 3 | ### CIS 198 Lecture 14 4 | 5 | --- 6 | ## Contributing to Rust 7 | 8 | - For the final project (or for fun!) you're allowed to contribute to the Rust language project, or 9 | to several other Rust projects. 10 | - Read the [contributing guide here][contrib] 11 | to get started. 12 | - Also check out the [issue tracker][issues]. 13 | - There are 2,288 issues open as of time of writing! 14 | - Filter by E-Easy, E-Medium, or E-Mentor to find good starting issues. 15 | 16 | [contrib]: (https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md) 17 | [issues]: (https://github.com/rust-lang/rust/issues) 18 | 19 | --- 20 | ## Contributing to Rust 21 | 22 | - There are three(ish) main parts of the Rust project: 23 | - `rustc`, the Rust compiler 24 | - `libstd`, the Rust standard library 25 | - documentation 26 | - If you want to contribute to core `rustc`, beware that you may find it very difficult! 27 | - There's no real distinction between `rustc` & `libstd` in the issue tracker, but it's usually 28 | pretty obvious what an issue is about. 29 | - If you wish to contribute to Rust for the final project, don't _only_ work on documentation. 30 | 31 | --- 32 | ### Etiquette 33 | 34 | - If you wish to contribute, make sure you also read and follow Rust's 35 | [Code of Conduct.](https://www.rust-lang.org/conduct.html) 36 | - Be polite. Be organized. Don't create unnecessary work for other people. 37 | Test your code well. Use GitHub properly (issues and pull requests). 38 | 39 | --- 40 | ### Fork-Pull Request Model 41 | 42 | - When you want to work on a feature, start by forking the Rust repo on Github. 43 | - When you've completed whatever feature you're working on, make a pull request from your fork 44 | against the main Rust repo, and await review. 45 | - You'll be assigned a reviewer by bors, the Rust bot. 46 | - Your reviewer may ask you to make changes, provide more tests, etc. to your code. 47 | - Once review is complete, your changes will be "rolled up" by bors into a changeset and merged into 48 | `master` when appropriate. 49 | 50 | --- 51 | ### RFCs 52 | 53 | - If you want to propose a _larger_ feature for Rust, Cargo, or Crates.io, you should create an 54 | _RFC_, or Request For Comments. 55 | - e.g. semantic/syntactic changes, additions to `std`, removing language features... 56 | - Before you submit an RFC, you may want to discuss it on the Rust internals forum, IRC, etc. and 57 | talk to the core team about it first. 58 | - This is suggested to help you flesh out ideas & make sure an RFC won't get rejected outright. 59 | - If you think your idea is still worth proposing, write a formal RFC using the template on the RFCs 60 | repo, [following the process here][rfcs]. 61 | 62 | [rfcs]: https://www.rust-lang.org/conduct.html 63 | 64 | --- 65 | ### Resources 66 | 67 | - [Contributing guide][contrib] 68 | - [Issue tracker][issues] 69 | - [/r/rust on Reddit][reddit] 70 | - [#rust on IRC][irc] 71 | - [Rust Internals Forum][internals] 72 | 73 | [contrib]: (https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md) 74 | [issues]: (https://github.com/rust-lang/rust/issues) 75 | [reddit]: (https://www.reddit.com/r/rust) 76 | [irc]: (https://client00.chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust) 77 | [internals]: (http://internals.rust-lang.org/) 78 | 79 | --- 80 | ## Rust Community Projects 81 | 82 | - [Servo](https://servo.org/) 83 | - [Piston](http://www.piston.rs/) 84 | - [Redox](http://www.redox-os.org/) 85 | - Others 86 | 87 | --- 88 | ### Servo 89 | 90 | - A parallel browser engine project developed in Rust by Mozilla. 91 | - [Contributing](https://github.com/servo/servo/blob/master/CONTRIBUTING.md) guide. 92 | - [Quick-Start](https://github.com/servo/servo/blob/master/HACKING_QUICKSTART.md) guide. 93 | 94 | --- 95 | ### Piston 96 | 97 | - A game engine under development in Rust. 98 | - [Contributing](https://github.com/PistonDevelopers/piston/blob/master/CONTRIBUTING.md) 99 | - [Quick-Start](https://github.com/PistonDevelopers/Piston-Tutorials/tree/master/getting-started) 100 | 101 | --- 102 | ### Redox 103 | 104 | - A brand-new operating system written in Rust! 105 | - [Contributing](https://github.com/redox-os/redox/blob/master/CONTRIBUTING.md) 106 | 107 | --- 108 | ### Other Projects 109 | 110 | - Look at [libs.rs](http://libs.rs/), [crates.fyi](https://crates.fyi/) and 111 | [Awesome Rust](https://github.com/kud1ing/awesome-rust) for more ideas. 112 | -------------------------------------------------------------------------------- /14/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "13 - Nightly Rust" 4 | --- 5 | -------------------------------------------------------------------------------- /15/content.md: -------------------------------------------------------------------------------- 1 | # Nightly Rust 2 | 3 | ### CIS 198 Lecture 15 4 | 5 | --- 6 | ## Nightly Rust 7 | 8 | - AKA all those cool features you aren't allowed to use on the homework. 9 | - Multirust: 10 | - `multirust update nightly`: download or update the nightly toolchain 11 | - `multirust override nightly` 12 | - `multirust override nightly-04-01` 13 | - With the standard rustup script: 14 | - `curl [rustup.sh] | sh -s -- --channel=nightly` 15 | 16 | --- 17 | ## Feature Gates 18 | 19 | - Unstable or experimental features are behind feature gates. 20 | 21 | ```rust 22 | #![feature(plugin_registrar, rustc_private)] 23 | ``` 24 | 25 | - Include the appropriate attributes at the top of your root module. 26 | 27 | --- 28 | ## Compiler Plugins 29 | 30 | - Add custom behavior during compilation process. 31 | - A few different categories that run at different times during compilation: 32 | - Syntax extension/procedural macro: code generation. 33 | - Lint pass: code verification (e.g. style checks, common errors). 34 | - LLVM pass: transformation or optimization of generated LLVM. 35 | 36 | ??? 37 | 38 | - Probably the biggest/most interesting feature gated feature 39 | - Linting: "unused variable warning" or "snake case" 40 | - LLVM: An intermediate step between "Rust source code" and "binary executable file" 41 | 42 | --- 43 | ## Compiler Plugins 44 | 45 | - To use a plugin in your crate: 46 | 47 | ```rust 48 | #![feature(plugin)] 49 | #![plugin(foo)] 50 | #![plugin(foo(bar, baz))] 51 | ``` 52 | 53 | - If arguments are present, rustc passes them to the plugin. 54 | - Don't use `extern crate ...`; this would link against the entire crate, 55 | including linking against all of its dependencies. 56 | 57 | --- 58 | ## Compiler Plugins 59 | 60 | - To write a plugin: 61 | 62 | ```rust 63 | // Unlock feature gates first. 64 | #![feature(plugin_registrar, rustc_private)] 65 | 66 | fn expand_hex_literals(cx: &mut ExtCtxt, sp: Span, 67 | args: &[TokenTree]) -> Box { 68 | /* Define the "hex" macro here. */ 69 | } 70 | 71 | 72 | // Register your macro here. 73 | #[plugin_registrar] 74 | pub fn plugin_registrar(reg: &mut Registry) { 75 | reg.register_macro("hex", expand_hex_literals); 76 | } 77 | ``` 78 | 79 | --- 80 | ### Procedural Macros 81 | 82 | - Similar to "normal" macros; call them with the same `foo!` pattern. 83 | - Typical macros generate code by matching against syntax patterns. 84 | - Compiler plugins run Rust code to manipulate the syntax tree directly. 85 | - Benefits: 86 | - More powerful code generation. 87 | - Compile-time input validation. 88 | - Custom error messages (reported on original source code, not generated code) 89 | 90 | ??? 91 | 92 | i.e. instead of generating code which calculates the value of a hex literal, 93 | you can directly translate a hex literal into a base-10 literal. 94 | 95 | --- 96 | ### Procedural Macros 97 | 98 | - Example: Docopt, a command line argument parser plugin. 99 | 100 | ```rust 101 | docopt!(Args derive Debug, " 102 | Naval Fate. 103 | 104 | Usage: 105 | naval_fate.py ship new ... 106 | naval_fate.py ship shoot 107 | 108 | Options: 109 | -v --verbose Verbose output. 110 | --speed= Speed in knots [default: 10]. 111 | "); 112 | ``` 113 | 114 | - Verify at compile time if you have a valid configuration. 115 | - Generates the **Args** struct, which **derives `Debug`**. 116 | - At runtime, this contains information about the flags provided. 117 | 118 | --- 119 | ### Procedural Macros 120 | 121 | - Very recent 122 | [RFC](https://github.com/nrc/rfcs/blob/proc-macros/text/0000-proc-macros.md) 123 | ([PR](https://github.com/rust-lang/rfcs/pull/1566)) 124 | proposing a major change to the macro system. 125 | - (Literally, the PR for this RFC was opened since I wrote 126 | last week's lecture and said there might be a new macro 127 | system in the future. -Kai) 128 | - Decoupling macros from compiler's internal `libsyntax`, using `libmacro` 129 | instead. 130 | 131 | ```rust 132 | #[macro] 133 | pub fn foo(TokenStream, &mut MacroContext) -> TokenStream; 134 | ``` 135 | 136 | --- 137 | ### Syntex-syntax 138 | 139 | - A pre-processing workaround for compiler plugins for stable Rust. 140 | - Part of `serde`, used by many other projects. 141 | - Uses Cargo support for `build.rs`: a pre-compilation "script". 142 | - Compiles and runs before the rest of your crate 143 | - `syntex` uses an external build of `libsyntax` to run any code generation that 144 | would be part of a compiler plugin. 145 | 146 | ??? 147 | 148 | The output of syntex is then built with the rest of the project: 149 | 150 | foo.rs.in -> syntex -> foo.rs -> crate built normally 151 | 152 | 153 | --- 154 | ### Lints 155 | 156 | - These plugins are run after code generation. 157 | - Lints traverse the syntax tree and examine particular nodes. 158 | - Throw errors or warnings when they come across certain patterns. 159 | - As simple as `snake_case`, or more complicated patterns, e.g.: 160 | 161 | ```rust 162 | if (x) { 163 | return true; 164 | } else { 165 | return false; 166 | } 167 | ``` 168 | 169 | - `rust-clippy` is a collection of common mistakes. 170 | 171 | ??? 172 | 173 | - A lot of what clippy does is catch over-complicated logic, such as using 174 | Mutexes when not needed, calling Clone on a Copy type, bad casting, etc. 175 | - Dead code 176 | 177 | --- 178 | ### LLVM Passes 179 | 180 | - LLVM is an intermediate stage between source code and assembly language. 181 | - LLVM-pass plugins let you manipulate the LLVM-generation step in compilation 182 | - Actual plugin must be written separately in C++. 183 | - Need to register with plugin registrar so it can be called at the 184 | appropriate time. 185 | - Examples: extremely low-level optimization, benchmarking. 186 | 187 | --- 188 | ## Inline Assembly 189 | 190 | - For the particularly daring, you can insert assembly directly into your 191 | programs! 192 | - Both feature gated _and_ unsafe. 193 | - Specify target architectures, with fallbacks. 194 | - Directly binds to LLVM's inline assembler expressions. 195 | 196 | ```rust 197 | #![feature(asm)] 198 | 199 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 200 | fn foo() { 201 | unsafe { 202 | asm!("NOP"); 203 | } 204 | } 205 | 206 | // other platforms 207 | #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 208 | fn foo() { /* ... */ } 209 | ``` 210 | 211 | ??? 212 | 213 | - Not too much to say about this. Probably most of you won't ever want to 214 | actually write inline assembly. 215 | - Use cfg attribute to target architectures. 216 | - Another reason to use LLVM: provides structure for inline assembly. 217 | 218 | --- 219 | ## No stdlib 220 | 221 | - Another crate-level attribute: `#![no_std]`. 222 | - Drops most of standard libary: threads, networking, heap allocation, I/O, etc. 223 | - What's left is the `core` crate: 224 | - No upstream or system libraries (including libc). 225 | - Primitive types, marker traits (`Clone`, `Copy`, `Send`, `Sync`), 226 | operators. 227 | - Low-level memory operations (`memcmp`, `memcpy`, `memswap`). 228 | - `Option`, `Result`. 229 | - Useful for building operating systems (Redox), embedded systems. 230 | 231 | --- 232 | ## No stdlib 233 | 234 | - `#![no_std]` also drops `fn main()`. 235 | - Annotate your starting function wtih `#[start]`. 236 | - Same function signature as main() in C. 237 | 238 | ```rust 239 | // Entry point for this program 240 | #[start] 241 | fn start(_argc: isize, _argv: *const *const u8) -> isize { 242 | 0 243 | } 244 | ``` 245 | 246 | --- 247 | ## Specialization 248 | 249 | - Generally, a trait or type may only have one `impl`. 250 | - Multiple `impl`s with different trait bounds are allowed, if they don't conflict. 251 | - Trait implementations for generic types may therefore restrictively constrain operations on those 252 | types. 253 | - Specialization enables you to provide multiple `impl`s for a type, so long as one is clearly 254 | _more specific_ than another. 255 | 256 | --- 257 | ## Specialization 258 | 259 | ```rust 260 | trait AddAssign { 261 | fn add_assign(&mut self, Rhs); 262 | } 263 | 264 | impl + Clone> AddAssign for T { 265 | fn add_assign(&mut self, rhs: R) { 266 | let tmp = self.clone() + rhs; 267 | *self = tmp; 268 | } 269 | } 270 | ``` 271 | 272 | ??? 273 | 274 | This implementation constrains `T`, as it _must_ be `Clone` to implement `AddAsign`. But, we may not 275 | want to clone `T` when performing this operation, and right now there's no way to specify that `T` 276 | doesn't need to be clone if we don't want it to be, especially since we _shouldn't_ have to clone 277 | `T` to perform a += operation (at least not all the time). Specialization will allow us to define a 278 | more specific `impl` for this trait over `T`, which can be used in cases where `T` doesn't need to 279 | be `Clone`. 280 | 281 | --- 282 | ## Specialization 283 | 284 | ```rust 285 | trait Extend { 286 | fn extend(&mut self, iterable: T) 287 | where T: IntoIterator; 288 | } 289 | ``` 290 | 291 | - Collections that implement the `Extend` trait can insert data from arbitrary iterators. 292 | - The definition above means that nothing can be assumed about `T` except that it can be turned into 293 | an iterator. 294 | - All code must insert elements one at a time! 295 | - E.g. extending a `Vec` with a slice could be done more efficiently, and the compiler can't always 296 | figure this out. 297 | 298 | --- 299 | ## Specialization 300 | 301 | - `Extend` could be specialized for a `Vec` and a slice like so. 302 | - `std` doesn't do this _yet_, but it's planned to do so soon. 303 | 304 | ```rust 305 | // General implementation (note the `default fn`) 306 | impl Extend for Vec where T: IntoIterator { 307 | default fn extend(&mut self, iterable: T) { 308 | // Push each element into the `Vec` one at a time 309 | } 310 | } 311 | 312 | // Specialized implementation 313 | impl<'a, A> Extend for Vec { 314 | fn extend(&mut self, iterable: &'a [A]) { 315 | // Use ptr::write to write the slice to the `Vec` en masse 316 | } 317 | } 318 | ``` 319 | 320 | --- 321 | ## Specialization 322 | 323 | - Specialization also allows for _default specialized_ trait implementations: 324 | 325 | ```rust 326 | trait Add { 327 | type Output; 328 | fn add(self, rhs: Rhs) -> Self::Output; 329 | fn add_assign(&mut self, rhs: Rhs);;;; 330 | } 331 | 332 | default impl Add for T { 333 | fn add_assign(&mut self, rhs: Rhs) { 334 | let tmp = self.clone() + rhs; 335 | *self = tmp; 336 | } 337 | } 338 | 339 | ``` 340 | 341 | ??? 342 | 343 | This is not the actual `Add` trait, but a modified one that requires you to specify `add_assign` as 344 | well. This was taken from the Specialization RFC text. 345 | 346 | This default impl does not mean that `Add` is implemented for all `Clone` types, but when you 347 | implement `Add` yourself on a type that is `Clone`, you can leave off `add_assign`, as the default 348 | implementation will generate its definition based on the specialized method for you. You can define 349 | default specialized implementations for the same trait for types with different type bounds, as well. 350 | 351 | --- 352 | ## Specialization 353 | 354 | - Currently in Rust, trait implementations may not overlap. 355 | 356 | ```rust 357 | trait Example { 358 | type Output; 359 | fn generate(self) -> Self::Output; 360 | } 361 | 362 | impl Example for T { 363 | type Output = Box; 364 | fn generate(self) -> Box { Box::new(self) } 365 | } 366 | 367 | impl Example for bool { 368 | type Output = bool; 369 | fn generate(self) -> bool { self } 370 | } 371 | ``` 372 | 373 | ??? 374 | 375 | These implementations of `Example` overlap-- `T` is a generalization of `bool`. 376 | We could use this to write the code below, which to the type checker looks like it might be okay, 377 | but due to monomorphization, `trouble` should generate a `bool`, which can't be dereferenced, so 378 | the whole thing falls apart. Trait overlap is a little bit hard to quantify without getting into a 379 | lot of detail about contra- co- and invariance, but the basic idea is that if you're implementing 380 | a trait over some type `T`, if you want to have a specialized 381 | 382 | ```rust 383 | fn trouble(t: T) -> Box { 384 | Example::generate(t) 385 | } 386 | 387 | fn weaponize() -> bool { 388 | let b: Box = trouble(true); 389 | *b 390 | } 391 | ``` 392 | 393 | --- 394 | ## Specialization 395 | 396 | - Specialization is designed to allow implementation overlap in specific ways. 397 | - `impl`s may overlap in concrete type or type constraints. 398 | - One `impl` must be more specific in some way than others. 399 | - This may mean different concrete types (`T` vs. `String`)... 400 | - ...or different type bounds (`T` vs. `T: Clone`) 401 | 402 | --- 403 | ## Specialization 404 | 405 | - Remember, with any of these trait implementations all dispatch is strictly static unless you're 406 | using trait objects. 407 | - Trait specialization should strictly improve the performance of your code! 408 | - Assuming you actually write everything correctly, of course. 409 | - Available on the Rust 1.9 Nightly: 410 | 411 | `#![feature(specialization)]` 412 | 413 | ??? 414 | 415 | Specialization also just landed for `.to_string()` for `&str`! Rejoice! No more using `.to_owned()` 416 | or wondering if `String::from()` is more efficient than `into()` or whatever! If you want more info 417 | on the implementation and full spec of specialization, check out the specialization RFC. It's 418 | really, really long, roughly 55 pages of text, but worth the read if you're interested in language 419 | design. 420 | 421 | --- 422 | ## `const` Functions 423 | 424 | - Motivation: types like `UnsafeCell` are more unsafe than they need to be. 425 | 426 | ```rust 427 | struct UnsafeCell { pub value: T } 428 | struct AtomicUsize { v: UnsafeCell } 429 | const ATOMIC_USIZE_INIT: AtomicUsize = AtomicUsize { 430 | v: UnsafeCell { value: 0 } 431 | }; 432 | ``` 433 | 434 | ??? 435 | 436 | `ATOMIC_USIZE_INIT` is a const value, so it must be _completely initialized_ at compile time. This 437 | means that you can't call any constructors for `UnsafeCell` in order to initialize it, since those 438 | require code to execute in order to be evaluated. The internals of types like `UnsafeCell` may now 439 | be accessed and mutated arbitrarily, and while it may appear that this is fine, since, hey, the type 440 | is already unsafe to begin with, this is really not a good property to be forced to introduce into 441 | your code simply because you can't execute code in `static` or `const` initializers. The "best" way 442 | to get around this limitation right now is to use a `static mut` variable and initialize it as soon 443 | as you possibly can, but that's inherently unsafe as well, so it's not really any better! 444 | 445 | --- 446 | ## `const` Functions 447 | 448 | - Proposed solution: `const fn`. 449 | - Kind of similar to `constexpr` from C++. 450 | - Certain functions and methods may be marked as `const`, indicating that they may be evaluated at 451 | compile time. 452 | - `const fn` expressions are pretty restrictive right now. 453 | - Arguments must be taken by value 454 | - No side effects are allowed (assignment, non-`const` function calls, etc.) 455 | - No instantiating types that implement `Drop` 456 | - No branching (if/else, loops) 457 | - No virtual dispatch 458 | 459 | ??? 460 | 461 | These are the "least-implemented" feature of all the ones in this lecture 462 | -------------------------------------------------------------------------------- /15/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "15 - Nightly Rust" 4 | --- 5 | -------------------------------------------------------------------------------- /16/content.md: -------------------------------------------------------------------------------- 1 | # Subtyping & Variance 2 | 3 | ### CIS 198 Lecture 16 4 | 5 | --- 6 | ## Higher-Rank Trait Bounds 7 | 8 | ```rust 9 | struct Closure { 10 | data: (u8, u16), 11 | func: F, 12 | } 13 | 14 | impl Closure where F: Fn(&(u8, u16)) -> &u8 { 15 | fn call(&self) -> &u8 { 16 | (self.fun)(&self.data) 17 | } 18 | } 19 | 20 | fn do_it(data: &(u8, u16)) -> &u8 { &data.0 } 21 | 22 | fn main() { 23 | let c = Closure { data: (0, 1), func: do_it, }; 24 | c.call(); 25 | } 26 | ``` 27 | 28 | ??? 29 | 30 | Closures in Rust are pretty magical when it comes to their underlying implementation. This code 31 | compiles fine, but has some lifetime weirdness. Let's desugar it and explictly annotate the lifetimes 32 | that the compiler inserts (more or less). Note that the code on the next slide doesn't compile in 33 | Rust because you can't annotate lifetimes that way, but the idea we're expressing is correct. 34 | 35 | --- 36 | ## Higher-Rank Trait Bounds 37 | 38 | ```rust 39 | struct Closure { 40 | data: (u8, u16), 41 | func: F, 42 | } 43 | 44 | impl Closure where F: Fn(&'??? (u8, u16)) -> &'??? u8 { 45 | fn call<'a>(&'a self) -> &'a u8 { 46 | (self.fun)(&self.data) 47 | } 48 | } 49 | 50 | fn do_it<'b>(data: &'b (u8, u16)) -> &'b u8 { &'b data.0 } 51 | 52 | fn main() { 53 | 'x: { 54 | let c = Closure { data: (0, 1), func: do_it, }; 55 | c.call(); 56 | } 57 | } 58 | ``` 59 | 60 | ??? 61 | 62 | Here, we can describe the lifetimes of everything in play _except_ for the references in use in the 63 | function we're storing inside the `Closure` struct. We have to provide _some_ lifetime bound for these 64 | references, but the lifetime can't be named until the body of `call` is entered, so we're stuck. 65 | Moreover, `call` has to work with _any_ lifetime that `&self` has at this point; we already know that 66 | this code compiles and works fine in Rust, so _how_ does it work? 67 | 68 | --- 69 | ## Higher-Rank Trait Bounds 70 | 71 | ```rust 72 | impl Closure where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8 { 73 | fn call<'a>(&'a self) -> &'a u8 { 74 | (self.fun)(&self.data) 75 | } 76 | } 77 | 78 | ``` 79 | 80 | ??? 81 | 82 | The compiler desugars this lifetime bound like so. You can read this as "where for all choices of 83 | the lifetime `a`, F is such and such type". This effectively produces an infinite list of trait bounds 84 | that `F` must satisfy. Generally, this doesn't come up very much. However, if you're doing something 85 | more complex with function pointers and closures, you may run into needing this syntax. Anywhere you 86 | may have a `Trait<'a>`, a `for<'a> Trait<'a>` is also allowed. It may not seem super obvious why you 87 | need these at all if you aren't thinking about lifetimes too hard. However, recall that lifetimes 88 | are kind of like type parameters-- an `&'a` and an `&'static` may not always be compatible! Without 89 | HRTBs, it would be hard to abstract over function types, as lifetime bounds would cause conflicts. 90 | 91 | --- 92 | ## Inheritance vs. Subtyping 93 | 94 | - Rust does not support structural inheritance. 95 | - No classes, virtual functions (sort of), method overriding, etc. 96 | - Subtyping in Rust derives exclusively from lifetimes. 97 | - Lifetimes may be partially ordered based on a containment relation. 98 | 99 | --- 100 | ## Lifetime Subtyping 101 | 102 | - Lifetime subtyping is in terms of the containment relationship: 103 | - If lifetime `a` contains (outlives) lifetime `b`, then `'a` is a subtype 104 | of `'b`. 105 | - In Rust syntax, this relationship is written as `'a: 'b`. 106 | 107 | ??? 108 | 109 | This relationship is somewhat counter-intuitive-- if `a` outlives `b`, why is 110 | `a` a subtype of `b`? `a` is larger than `b`, but it's a subtype; why do we 111 | express this relationship this way? From a typing perspective, think of it like 112 | this: if you want an `&'a str` and I give you an `&'static str`, that should be 113 | totally fine. Similarly, in an OO world with structural inheritance, if you have 114 | a collection of type `Animal` and I give you a `Cat` to insert into it, that 115 | should be fine as well. Hence, lifetimes express the same subtyping 116 | relationship. If you have a higher-rank lifetime (e.g. from a higher-rank trait 117 | bound), they're also subtypes of every concrete lifetime, since they can specify 118 | any arbitrary lifetime. 119 | 120 | --- 121 | ## Type Variance 122 | 123 | - Variance is a property of type constructors with respect to their arguments. 124 | - Type constructors in Rust can be either _variant_ or _invariant_ over their types. 125 | - Variance: `F` is _variant_ over `T` if `T` being a subtype of `U` implies 126 | `F` is a subtype of `F`. 127 | - Subtyping "passes through". 128 | - Invariance: `F` is _invariant_ over `T` in all other cases. 129 | - No subtyping relation can be derived. 130 | 131 | ??? 132 | 133 | Rust's variance relation is actually a covariance relation; other languages also 134 | have contravariance over certain types, but Rust doesn't except over functions, 135 | which may change in the future. For reference, `fn(T)` is contravariant in `T`. 136 | However, because traits don't have inferred variance, `Fn(T)` is invariant in 137 | `T`. 138 | 139 | --- 140 | ## Type Variance 141 | 142 | - `&'a T` is variant over `'a` and `T` 143 | - As is `*const T`. 144 | - `&'a mut T` is variant over `'a` but invariant over `T`. 145 | - `Fn(T) -> U` is invariant over `T` but variant over `U`. 146 | - `Box`, `Vec`, etc. are all variant over `T` 147 | - `Cell` and any other types with interior mutability are invariant over `T` 148 | - As is `*mut T`. 149 | 150 | --- 151 | ## Type Variance 152 | 153 | - Why do the above properties hold? 154 | 155 | ??? 156 | 157 | `&'a T` is variant over `'a` for reasons already discussed. Why over `T`? 158 | An `&&'static str` is appropriate when an `&&'a str` would be. `&'a mut T`'s 159 | lifetime variance also makes sense, but its type invariance might not. Let's 160 | look at a code example... 161 | 162 | --- 163 | ### `&'a mut T` Type Invariance 164 | 165 | ```rust 166 | fn overwrite(input: &mut T, new: &mut T) { 167 | *input = *new; 168 | } 169 | 170 | let mut forever: &'static str = "hello"; 171 | { 172 | let s = String::from("world"); 173 | overwrite(&mut forever, &mut &*s); 174 | } 175 | println!("{}", forever); 176 | ``` 177 | 178 | - In general, if variance would allow you to store a short-lived value into a 179 | longer-lived slot, you must have invariance. 180 | 181 | ??? 182 | 183 | Everything about this code looks fine, except it actually ends up printing freed 184 | memory at the end. If `&mut T` were variant over `T`, then `&mut &'static str` 185 | would be a subtype of `&mut &'a str`, and you could downgrade the `'static` 186 | lifetime to `'a`. This would allow you to overwrite `forever` with a `s`, drop 187 | `s` after the inner scope exits in the above program, and then still access its 188 | memory via `forever`. This is tricky, since you have to remember that lifetimes 189 | are _part of_ types, and therefore their variance must be considered in the `T` 190 | slot of other references. `&'a mut T` is allowed to be variant over `'a` because 191 | `'a` is a property of the reference, but `T` is borrowed by the reference. If 192 | the `'a` changes, only the reference will know; if you change the `T`, somebody 193 | else will know, so you can't do this. "The `'a` is owned, the `T` is borrowed." 194 | 195 | --- 196 | ## Type Variance 197 | 198 | - `Box` and `Vec` are variant over `T`, even though this looks like it breaks 199 | the rule we just defined. 200 | - Because you can only mutate a `Box` via an `&mut` reference, they become 201 | invariant when mutated! 202 | - This prevents you from storing shorter-lived types into longer-lived 203 | containers. 204 | - Cell types need invariance over `T` for the same reason `&mut T` does. 205 | - Without invariance, you could smuggle shorter-lived types into 206 | longer-lived cells. 207 | 208 | --- 209 | ## Type Variance 210 | 211 | - What about the variance of types you define yourself? 212 | - Struct `Foo` contains a field of type `A`... 213 | - `Foo` is variant over `A` if all uses of `A` are variant. 214 | - Otherwise, it's invariant over `A`. 215 | 216 | --- 217 | ## Type Variance 218 | 219 | ```rust 220 | struct Foo<'a, 'b, A: 'a, B: 'b, C, D, E, F, G, H> { 221 | a: &'a A, // variant over 'a and A 222 | b: &'b mut B, // variant over 'b, invariant over B 223 | c: *const C, // variant over C 224 | d: *mut D, // invariant over D 225 | e: Vec, // variant over E 226 | f: Cell, // invariant over F 227 | g: G, // variant over G 228 | h1: H, // would be variant over H... 229 | h2: Cell, // ...but Cell forces invariance 230 | } 231 | ``` 232 | 233 | --- 234 | ## Type Variance 235 | 236 | - A good way to think about lifetime variance is in terms of ownership. 237 | - If someone else owns a value, and you own a reference to it, the reference can 238 | be variant over its lifetime, but might not be variant over the value's 239 | type. 240 | -------------------------------------------------------------------------------- /16/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "16 - Higher-Rank Trait Bounds" 4 | --- 5 | -------------------------------------------------------------------------------- /17/content.md: -------------------------------------------------------------------------------- 1 | # Borrowing & Owning 2 | 3 | ### CIS 198 Lecture 17 4 | 5 | --- 6 | ## Borrowing vs. Owning 7 | 8 | - Consider this struct: 9 | 10 | ```rust 11 | struct StrToken<'a> { 12 | raw: &'a str, 13 | } 14 | 15 | impl<'a> StrToken<'a> { 16 | pub fn new(raw: &'a str) -> StrToken<'a> { 17 | StrToken { raw: raw, } 18 | } 19 | } 20 | 21 | // ... 22 | 23 | let secret: String = load_secret("api.example.com"); 24 | let token = StrToken::new(&secret[..]); 25 | ``` 26 | 27 | *Code and examples taken from [From &str to Cow](http://blog.jwilm.io/from-str-to-cow) 28 | 29 | ??? 30 | 31 | This struct works fine for `&'static str`s, but is pretty inflexible. The use 32 | case this was presented in was one where `StrToken` represents an authentication 33 | token or application secret; in this case, you're basically forced to store the 34 | secret key in the application's binary, since this doesn't let you load it 35 | ad-hoc at runtime, e.g. from a file. Also, in the last two lines of code, we've 36 | forced the token to only live as long as `secret`, so `token` can't escape the 37 | current stack frame, as `secret` will no longer be valid if it does. This whole 38 | implementation is silly and we'd like to be able to do it differently... 39 | 40 | --- 41 | ## Borrowing vs. Owning 42 | 43 | ```rust 44 | struct StringToken { 45 | raw: String, 46 | } 47 | 48 | impl StringToken { 49 | pub fn new(raw: String) -> StringToken { 50 | StringToken { raw: raw, } 51 | } 52 | } 53 | ``` 54 | 55 | ??? 56 | 57 | This basically solves the problem we had before by switching out an `&str` for a 58 | `String`-- now we can load our secret at runtime _and_ we don't have to worry 59 | about lifetimes. Because `StringToken` owns its contents, it is guaranteed that 60 | its contents will _always be valid_. This is convenient, but is also not the 61 | best implementation. For one, we can't make `StringToken`s out of raw strings 62 | without _first_ coercing them into `String`s, which is not great. We could 63 | design this to do the `&str -> String` coercion for us, but that's also not a 64 | good solution, since we'd either have to make an additional constructor, or 65 | force an additional heap allocation in certain cases. If we make one constructor 66 | that just takes an `&str`, anyone who already has a `String` has to slice the 67 | `String` up into an `&str` and then allocate it on the heap _again_, even though 68 | they've _already done that_ by having a `String`. If we make two constructors, 69 | then we're basically duplicating code with only minor differences, which is 70 | unergonomic. The whole thing is silly and inefficient, and there really should 71 | be a way around it without having to settle for either of these solutions. 72 | 73 | --- 74 | ### `std::convert::Into` & `std::convert::From` 75 | 76 | ```rust 77 | pub trait Into { 78 | fn into(self) -> T; 79 | } 80 | 81 | pub trait From { 82 | fn from(T) -> Self; 83 | } 84 | 85 | impl Into for T where U: From { /* ... */ } 86 | ``` 87 | 88 | ??? 89 | 90 | `Into` is, guess what, a trait for converting types into other types by 91 | consuming the original value and producing a new one. Some standard library 92 | types implement `Into` in various ways, e.g. `String` implements `Into>` 93 | so you can get at the raw bytes behind a `String`. There's also a symmetric 94 | trait `From`, that is the opposite of `Into`. In fact, if `T` can be converted 95 | `Into`, then `U` can be created `From`. We're going to use these traits to 96 | build up a new abstraction that will let us more effectively solve the 97 | `&str`-`String` duality we were just dealing with. 98 | 99 | --- 100 | ## Borrowing \/ Owning 101 | 102 | ```rust 103 | struct Token { 104 | raw: String, 105 | } 106 | 107 | impl Token { 108 | pub fn new>(raw: S) -> Token 109 | Token { raw: raw.into(), } 110 | } 111 | } 112 | ``` 113 | 114 | ??? 115 | 116 | With the help of `Into`, we ensure our argument can always be converted into a 117 | `String`, and then convert it in the constructor. `Into` is always 118 | implemented for `T` itself, so this works fine with `String`s. `&str` and 119 | `String` have `From`/`Into` `impl`s for each other because of the last generic 120 | `impl` on the previous slide; if you look for them in the standard library, you 121 | won't find them directly. So, great, we've made this much more convenient to 122 | use! However, we still haven't solved the allocation problem: an `&str` argument 123 | still has to be converted into a `String`, which costs us. 124 | 125 | --- 126 | ## Bovine Intervention! 127 | 128 | ```rust 129 | pub enum Cow<'a, B> where B: 'a + ToOwned + ?Sized { 130 | Borrowed(&'a B), 131 | Owned(B::Owned), 132 | } 133 | ``` 134 | 135 | ??? 136 | 137 | `Cow`, short for "clone-on-write", is a type of smart pointer enum thing. It's a 138 | little bit hard to describe. The type is constrained by some lifetime `'a` and a 139 | generic type `B`. `B` is constrained by `'a + ToOwned + ?Sized`, which means 140 | this: 141 | - `'a`: `B` cannot contain a lifetime shorter than `'a` 142 | - `ToOwned`: `B` must implement the `ToOwned` trait, which describes how 143 | to convert it to an owned value 144 | - `?Sized`: `B` is allowed to be unsized at compile time, which 145 | basically just means you can use trait objects with `Cow`s 146 | `Cow` has two variants: 147 | - `Borrowed(&'a B)`: a reference to a `B`, bound to the lifetime of the 148 | trait bound 149 | - `Owned(B::Owned)`: the associated type from the `ToOwned` trait; this 150 | variant owns its value (obviously) 151 | When you want to mutate a `Cow` (gosh that sounds mad science-y), call `to_mut` 152 | on it. This causes its internal representation to be converted to the `Owned` 153 | variant, hence the clone-on-write semantics. Note that this causes an 154 | allocation. 155 | 156 | Aside: there's a now-deprecated trait called `IntoCow` which I think is probably 157 | the best trait name in `std`. 158 | 159 | --- 160 | ## Bovine Intervention! 161 | 162 | ```rust 163 | struct Token<'a> { 164 | raw: Cow<'a, str>, 165 | } 166 | 167 | impl<'a> Token<'a> { 168 | pub fn new>(raw: S) -> Token<'a> 169 | Token { raw: raw.into(), } 170 | } 171 | } 172 | 173 | // ... 174 | 175 | let token = Token::new(Cow::Borrowed("it's a secret")); 176 | let secret: String = load_secret("api.example.com"); 177 | let token = Token::new(Cow::Owned(secret)); 178 | ``` 179 | 180 | ??? 181 | 182 | Now we can pass either a `String` or an `&str` into our struct just fine, and 183 | `&str`s won't trigger an allocation. The lifetime bound on an `&str` still needs 184 | to be `'static` for it to escape the current stack frame, but we can't really 185 | solve that problem anyway. We can also send `Token`s across threads, so long as 186 | they're `'static`. Pretty nice, right? 187 | 188 | --- 189 | ## Recap 190 | 191 | - If you want to have the potential to own _or_ borrow a value in a struct, 192 | `Cow` is right for you. 193 | - `Cow` can be used with more than just `&str`/`String`, though this is a very 194 | common usage. 195 | - When you mutate a `Cow`, it might have to allocate and become its `Owned` 196 | variant. 197 | - The type inside of the `Cow` must be convertible to some owned form. 198 | -------------------------------------------------------------------------------- /17/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "16 - Higher-Rank Trait Bounds" 4 | --- 5 | -------------------------------------------------------------------------------- /18/content.md: -------------------------------------------------------------------------------- 1 | # Cross-Compilation 2 | 3 | ### CIS 198 Lecture 18 4 | 5 | --- 6 | 7 | ## [Rustup](https://www.rustup.rs/) (beta) 8 | 9 | - Successor to Multirust. 10 | - Currently in beta (try it out! - but not until after your projects.) 11 | - Written in Rust, not shell - works natively on Windows! 12 | 13 | - Still allows you to easily maintain multiple toolchains. 14 | - e.g. `stable`, `beta`, `nightly-2016-04-15`, `1.8.0` 15 | 16 | - Supports cross-compilation nicely! 17 | - Can download cross-compilation targets & `std` for other targets. 18 | - `rustup target list` - OS X, iOS, Windows, Android, etc. 19 | - `rustup target add x86_64-pc-windows-gnu` 20 | 21 | - Can still `override` the toolchain for a particular project: 22 | - `rustup override add nightly` - overrides current directory 23 | - `rustup override list` - lists overrides 24 | 25 | - Run commands with a particular toolchain: 26 | - `rustup run nightly COMMAND` 27 | 28 | --- 29 | 30 | ## Linux to Windows 31 | 32 | - [Minimal demo](https://github.com/cis198-2016s/demo-xcompile-win64) 33 | - Requires MinGW-w64. 34 | 35 | - Add the 64-bit Windows target: 36 | - `rustup target add x86_64-pc-windows-gnu` 37 | 38 | - Configure Cargo to target Windows and link using the 39 | MinGW-w64 linker. 40 | - `.cargo/config`: 41 | ```toml 42 | [target.x86_64-pc-windows-gnu] 43 | linker = "/usr/bin/x86_64-w64-mingw32-gcc" 44 | ar = "/usr/x86_64-w64-mingw32-ar" 45 | 46 | [build] 47 | target = "x86_64-pc-windows-gnu" 48 | ``` 49 | - In the future, this may be handled by: 50 | - `rustup override add stable-x86_64-pc-windows-gnu` 51 | - (Currently doesn't quite work for me.) 52 | 53 | --- 54 | 55 | ## Linux to Windows 56 | 57 | - `cargo build`! Build appears at: 58 | - `target/x86_64-pc-windows-gnu/debug/xcompile-win.exe` 59 | - `PE32+ executable (console) x86-64, for MS Windows` 60 | 61 | - Executable can be run on Windows (or under Wine). 62 | - Demo 63 | 64 | --- 65 | 66 | ## Linux to Android 67 | 68 | - [Minimal demo](https://github.com/cis198-2016s/demo-xcompile-android) 69 | 70 | - Rustup has a target for this: `arm-linux-androideabi` 71 | - But this will only get us a bare binary - not an APK. 72 | - `ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV)` 73 | 74 | - For APKs, we can use `cargo-apk` ([announcement](https://users.rust-lang.org/t/announcing-cargo-apk/5501)). 75 | - Compiles Rust code to a shared object (`.so`) file 76 | - Uses [android-rs-glue](https://github.com/tomaka/android-rs-glue). 77 | - Simple Java template for loading a shared object (`.so`) file and dynamically links it from Java. 78 | - Builds using Android build tools. 79 | - Host must be Linux x86\_64 (currently). 80 | 81 | --- 82 | 83 | ## Linux to Android 84 | 85 | - `cargo install cargo-apk` 86 | 87 | - `Cargo.toml`: 88 | ```toml 89 | [package.metadata.android] 90 | label = "xcompile-198" 91 | 92 | [dependencies] 93 | android_glue = "0.1.3" 94 | ``` 95 | 96 | - Build and install: 97 | ```sh 98 | export ANDROID_SDK_HOME=/opt/android-sdk 99 | export NDK_HOME=/opt/android-ndk 100 | cargo apk install 101 | ``` 102 | - Demo 103 | -------------------------------------------------------------------------------- /18/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: slides 3 | title: "18 - Cross-Compilation" 4 | --- 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'github-pages' 3 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | RedCloth (4.2.9) 5 | activesupport (4.2.5) 6 | i18n (~> 0.7) 7 | json (~> 1.7, >= 1.7.7) 8 | minitest (~> 5.1) 9 | thread_safe (~> 0.3, >= 0.3.4) 10 | tzinfo (~> 1.1) 11 | addressable (2.3.8) 12 | blankslate (2.1.2.4) 13 | classifier-reborn (2.0.4) 14 | fast-stemmer (~> 1.0) 15 | coffee-script (2.4.1) 16 | coffee-script-source 17 | execjs 18 | coffee-script-source (1.10.0) 19 | colorator (0.1) 20 | ethon (0.8.0) 21 | ffi (>= 1.3.0) 22 | execjs (2.6.0) 23 | faraday (0.9.2) 24 | multipart-post (>= 1.2, < 3) 25 | fast-stemmer (1.0.2) 26 | ffi (1.9.10) 27 | gemoji (2.1.0) 28 | github-pages (40) 29 | RedCloth (= 4.2.9) 30 | github-pages-health-check (= 0.5.3) 31 | jekyll (= 2.4.0) 32 | jekyll-coffeescript (= 1.0.1) 33 | jekyll-feed (= 0.3.1) 34 | jekyll-gist (= 1.4.0) 35 | jekyll-mentions (= 0.2.1) 36 | jekyll-paginate (= 1.1.0) 37 | jekyll-redirect-from (= 0.8.0) 38 | jekyll-sass-converter (= 1.3.0) 39 | jekyll-sitemap (= 0.9.0) 40 | jemoji (= 0.5.0) 41 | kramdown (= 1.9.0) 42 | liquid (= 2.6.2) 43 | maruku (= 0.7.0) 44 | mercenary (~> 0.3) 45 | pygments.rb (= 0.6.3) 46 | rdiscount (= 2.1.8) 47 | redcarpet (= 3.3.3) 48 | terminal-table (~> 1.4) 49 | github-pages-health-check (0.5.3) 50 | addressable (~> 2.3) 51 | net-dns (~> 0.8) 52 | public_suffix (~> 1.4) 53 | typhoeus (~> 0.7) 54 | html-pipeline (1.9.0) 55 | activesupport (>= 2) 56 | nokogiri (~> 1.4) 57 | i18n (0.7.0) 58 | jekyll (2.4.0) 59 | classifier-reborn (~> 2.0) 60 | colorator (~> 0.1) 61 | jekyll-coffeescript (~> 1.0) 62 | jekyll-gist (~> 1.0) 63 | jekyll-paginate (~> 1.0) 64 | jekyll-sass-converter (~> 1.0) 65 | jekyll-watch (~> 1.1) 66 | kramdown (~> 1.3) 67 | liquid (~> 2.6.1) 68 | mercenary (~> 0.3.3) 69 | pygments.rb (~> 0.6.0) 70 | redcarpet (~> 3.1) 71 | safe_yaml (~> 1.0) 72 | toml (~> 0.1.0) 73 | jekyll-coffeescript (1.0.1) 74 | coffee-script (~> 2.2) 75 | jekyll-feed (0.3.1) 76 | jekyll-gist (1.4.0) 77 | octokit (~> 4.2) 78 | jekyll-mentions (0.2.1) 79 | html-pipeline (~> 1.9.0) 80 | jekyll (~> 2.0) 81 | jekyll-paginate (1.1.0) 82 | jekyll-redirect-from (0.8.0) 83 | jekyll (>= 2.0) 84 | jekyll-sass-converter (1.3.0) 85 | sass (~> 3.2) 86 | jekyll-sitemap (0.9.0) 87 | jekyll-watch (1.3.0) 88 | listen (~> 3.0) 89 | jemoji (0.5.0) 90 | gemoji (~> 2.0) 91 | html-pipeline (~> 1.9) 92 | jekyll (>= 2.0) 93 | json (1.8.3) 94 | kramdown (1.9.0) 95 | liquid (2.6.2) 96 | listen (3.0.5) 97 | rb-fsevent (>= 0.9.3) 98 | rb-inotify (>= 0.9) 99 | maruku (0.7.0) 100 | mercenary (0.3.5) 101 | mini_portile2 (2.0.0) 102 | minitest (5.8.3) 103 | multipart-post (2.0.0) 104 | net-dns (0.8.0) 105 | nokogiri (1.6.7.1) 106 | mini_portile2 (~> 2.0.0.rc2) 107 | octokit (4.2.0) 108 | sawyer (~> 0.6.0, >= 0.5.3) 109 | parslet (1.5.0) 110 | blankslate (~> 2.0) 111 | posix-spawn (0.3.11) 112 | public_suffix (1.5.3) 113 | pygments.rb (0.6.3) 114 | posix-spawn (~> 0.3.6) 115 | yajl-ruby (~> 1.2.0) 116 | rb-fsevent (0.9.6) 117 | rb-inotify (0.9.5) 118 | ffi (>= 0.5.0) 119 | rdiscount (2.1.8) 120 | redcarpet (3.3.3) 121 | safe_yaml (1.0.4) 122 | sass (3.4.20) 123 | sawyer (0.6.0) 124 | addressable (~> 2.3.5) 125 | faraday (~> 0.8, < 0.10) 126 | terminal-table (1.5.2) 127 | thread_safe (0.3.5) 128 | toml (0.1.2) 129 | parslet (~> 1.5.0) 130 | typhoeus (0.8.0) 131 | ethon (>= 0.8.0) 132 | tzinfo (1.2.2) 133 | thread_safe (~> 0.1) 134 | yajl-ruby (1.2.1) 135 | 136 | PLATFORMS 137 | ruby 138 | 139 | DEPENDENCIES 140 | github-pages 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slides for CIS 198: Rust Programming 2 | 3 | Read these slides online by following links from the [course schedule](http://cis198-2016s.github.io/schedule/). 4 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | # Site settings 2 | title: "CIS 198" 3 | email: "cis198@cis.upenn.edu" 4 | baseurl: "/slides" 5 | url: "http://cis198-2016s.github.io" 6 | github_username: cis198-2016s 7 | 8 | # Build settings 9 | markdown: kramdown 10 | -------------------------------------------------------------------------------- /_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% if page.title %}{{ page.title }} - {{ site.title }}{% else %}{{ site.title }}{% endif %} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /_layouts/slides.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% include head.html %} 4 | 5 | 8 | 9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /css/common.css: -------------------------------------------------------------------------------- 1 | a { 2 | color: #428bca; 3 | text-decoration: none; 4 | } 5 | 6 | a:hover, a:focus { 7 | color: #2a6496; 8 | text-decoration: underline; 9 | } 10 | 11 | .stealth { 12 | text-decoration: none; 13 | color: inherit; 14 | } 15 | 16 | .btn { 17 | color: #fff; 18 | background-color: #428bca; 19 | border-color: #357ebd; 20 | border: 1px solid transparent; 21 | border-radius: 4px; 22 | height: 1.5em; 23 | width: 100%; 24 | padding: 1px; 25 | text-align: center; 26 | display: block; 27 | } 28 | 29 | .btn:hover, .btn:focus, .btn:active { 30 | color: #fff; 31 | background-color: #3276b1; 32 | border-color: #285e8e; 33 | text-decoration: none; 34 | } 35 | 36 | .btn img { 37 | height: 1.25em; 38 | } 39 | 40 | hr { 41 | border: 0; 42 | height: 1px; 43 | background: #ccc; 44 | } 45 | 46 | table { 47 | border-collapse: collapse; 48 | } 49 | 50 | td, th, tr { 51 | padding: 3px 4px; 52 | border: 1px solid gray; 53 | } 54 | 55 | /* run button on rust snippets */ 56 | code.rust { position: relative; } 57 | a.test-arrow { 58 | display: inline-block; 59 | position: absolute; 60 | 61 | background-color: #4e8bca; 62 | color: #f5f5f5; 63 | padding: 5px 10px 5px 10px; 64 | border-radius: 5px; 65 | top: 5px; 66 | right: 5px; 67 | } 68 | -------------------------------------------------------------------------------- /css/slides.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: emoji; 3 | 4 | src: local('Noto Color Emoji'), 5 | local('Noto Emoji'), 6 | local('Apple Color Emoji'), 7 | local('Android Emoji'), 8 | local('Segoe UI'), 9 | local(EmojiSymbols), 10 | local(Symbola); 11 | 12 | /* Emoji unicode blocks */ 13 | unicode-range: U+1F300-1F5FF, U+1F600-1F64F, U+1F680-1F6FF, U+2600-26FF; 14 | } 15 | 16 | body { 17 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif, emoji; 18 | font-size: 18pt; 19 | } 20 | 21 | h1, h2, h3, h4, h5, h6 { 22 | text-align: center; 23 | } 24 | 25 | h1, h2, h3 { 26 | height: 80px; 27 | margin: 20px 0px; 28 | } 29 | 30 | strong, b { 31 | font-weight: 600; 32 | } 33 | 34 | h5 { 35 | font-size: 100%; 36 | font-weight: 600; 37 | margin: 0; 38 | } 39 | 40 | h6 { 41 | font-size: 100%; 42 | font-weight: 400; 43 | margin: 0; 44 | } 45 | 46 | img { 47 | display: block; 48 | margin-left: auto; 49 | margin-right: auto; 50 | width: 50%; 51 | } 52 | 53 | .remark-code { 54 | font-size: 16pt; 55 | } 56 | .remark-inline-code { 57 | font-size: 110%; 58 | color: #8D1A38; 59 | } 60 | .remark-slide-content { 61 | font-size: inherit; 62 | padding: 1em 3.5em; 63 | } 64 | 65 | pre, code { 66 | font-family: "Ubuntu Mono", "Consolas", monospace, emoji; 67 | } 68 | 69 | li, ul { 70 | margin: 0.25em 0; 71 | } 72 | ul, pre, li > p { 73 | margin: 0; 74 | } 75 | 76 | li li { 77 | font-size: 90%; 78 | } 79 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cis198-2016s/slides/0961baa3710985ff53f5ca60796b89cbeffd6a3c/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /js/playpen.198.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | // 11 | // Modified slightly for CIS 198. Changes released under the original license. 12 | 13 | /*jslint browser: true, es5: true */ 14 | /*globals $: true, rootPath: true */ 15 | 16 | document.addEventListener('DOMContentLoaded', function() { 17 | 'use strict'; 18 | 19 | if (!window.playgroundUrl) { 20 | return; 21 | } 22 | 23 | var featureRegexp = new RegExp('^\s*#!\\[feature\\(\.*?\\)\\]'); 24 | var elements = document.querySelectorAll('code.rust'); 25 | 26 | Array.prototype.forEach.call(elements, function(el) { 27 | el.onmouseover = function(e) { 28 | if (el.contains(e.relatedTarget)) { 29 | return; 30 | } 31 | 32 | var a = document.createElement('a'); 33 | a.setAttribute('class', 'test-arrow'); 34 | a.textContent = 'Run'; 35 | 36 | var code = ""; 37 | for (var i = 0; i < el.childNodes.length; i++) { 38 | code += el.childNodes[i].textContent + "\n"; 39 | } 40 | code = code.trim(); 41 | 42 | if (!code.match(/^fn main\b/m)) { 43 | // If the code doesn't have `fn main()`, add it 44 | code = "fn main() {\n" + code.replace(/^/gm, " ") + "\n}"; 45 | } 46 | code = "#![allow(dead_code)]\n" + code; 47 | 48 | var channel = ''; 49 | if (featureRegexp.test(code)) { 50 | channel = '&version=nightly'; 51 | } 52 | 53 | a.setAttribute('href', window.playgroundUrl + '?code=' + 54 | encodeURIComponent(code) + channel); 55 | a.setAttribute('target', '_blank'); 56 | 57 | el.appendChild(a); 58 | }; 59 | 60 | el.onmouseout = function(e) { 61 | if (el.contains(e.relatedTarget)) { 62 | return; 63 | } 64 | 65 | el.removeChild(el.querySelectorAll('a.test-arrow')[0]); 66 | }; 67 | }); 68 | }); 69 | --------------------------------------------------------------------------------

(&mut self, predicate: P) -> Option 582 | where P: FnMut(Self::Item) -> bool; 583 | ``` 584 | 585 | - Try to find the first item in the iterator that matches the `predicate` function. 586 | - `find` returns the item itself. 587 | - `position` returns the item's index. 588 | - On failure, both return a `None`. 589 | 590 | --- 591 | ## `skip` 592 | 593 | ```rust 594 | fn skip(self, n: usize) -> Skip; 595 | ``` 596 | - Creates an iterator that skips its first `n` elements. 597 | 598 | --- 599 | ## `zip` 600 | 601 | ```rust 602 | fn zip(self, other: U) -> Zip 603 | where U: IntoIterator; 604 | ``` 605 | 606 | - Takes two iterators and zips them into a single iterator. 607 | - Invoked like `a.iter().zip(b.iter())`. 608 | - Returns pairs of items like `(ai, bi)`. 609 | - The shorter iterator of the two wins for stopping iteration. 610 | 611 | --- 612 | ## `any` & `all` 613 | 614 | ```rust 615 | fn any(&mut self, f: F) -> bool 616 | where F: FnMut(Self::Item) -> bool; 617 | 618 | fn all(&mut self, f: F) -> bool 619 | where F: FnMut(Self::Item) -> bool; 620 | ``` 621 | 622 | - `any` tests if any element in the iterator matches the input function 623 | - `all` tests all elements in the iterator match the input function 624 | - Logical OR vs. logical AND. 625 | 626 | --- 627 | ## `enumerate` 628 | 629 | ```rust 630 | fn enumerate(self) -> Enumerate; 631 | ``` 632 | 633 | - Want to iterate over a collection by item and index? 634 | - Use `enumerate`! 635 | - This iterator returns `(index, value)` pairs. 636 | - `index` is the `usize` index of `value` in the collection. 637 | 638 | --- 639 | ## Iterator Adapters 640 | 641 | - Adapters operate on an iterator and return a new iterator. 642 | - Adapters are often _lazy_ -- they don't evaluate unless you force them to! 643 | - You must explicitly call some iterator consumer on an adapter or use it in a 644 | `for` loop to cause it to evaluate. 645 | 646 | --- 647 | ## `map` 648 | 649 | ```rust 650 | fn map(self, f: F) -> Map 651 | where F: FnMut(Self::Item) -> B; 652 | 653 | let vs = vec![1,2,3,4,5]; 654 | let twice_vs: Vec<_> = vs.iter().map(|x| x * 2).collect(); 655 | ``` 656 | 657 | - `map` takes a function and creates an iterator that calls the function on each 658 | element 659 | - Abstractly, it takes a `Collection` and a function of `A -> B` and 660 | returns a `Collection` 661 | - (`Collection` is not a real type) 662 | 663 | --- 664 | ## `take` & `take_while` 665 | 666 | ```rust 667 | fn take(self, n: usize) -> Take; 668 | 669 | fn take_while