├── 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 | 
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