ship generated workspace files for the benefit of non-Linux users
[rust-101.git] / workspace / src / part06.rs
1 // ***Remember to enable/add this part in `main.rs`!***
2
3 // Rust-101, Part 06: Copy, Lifetimes
4 // ==================================
5
6 // We continue to work on our `BigInt`, so we start by importing what we already established.
7 use part05::BigInt;
8
9 // With `BigInt` being about numbers, we should be able to write a version of `vec_min`
10 // that computes the minimum of a list of `BigInt`. First, we have to write `min` for `BigInt`.
11 impl BigInt {
12     fn min_try1(self, other: Self) -> Self {
13         // Just to be sure, we first check that both operands actually satisfy our invariant. `debug_assert!` is a
14         // macro that checks that its argument (must be of type `bool`) is `true`, and panics otherwise. It gets
15         // removed in release builds, which you do with `cargo build --release`.
16         debug_assert!(self.test_invariant() && other.test_invariant());
17         // Now our assumption of having no trailing zeros comes in handy:
18         // If the lengths of the two numbers differ, we already know which is larger.
19         if self.data.len() < other.data.len() {
20             self
21         } else if self.data.len() > other.data.len() {
22             other
23         } else {
24             // **Exercise 06.1**: Fill in this code.
25             unimplemented!()
26         }
27     }
28 }
29
30 // Now we can write `vec_min`. However, in order to make it type-check, we have to make a full (deep) copy of e
31 // by calling `clone()`.
32 fn vec_min(v: &Vec<BigInt>) -> Option<BigInt> {
33     let mut min: Option<BigInt> = None;
34     for e in v {
35         let e = e.clone();
36         min = Some(match min {
37             None => e,
38             Some(n) => e.min_try1(n)
39         });
40     }
41     min
42 }
43 // Now, what's happening here? Why do we have to clone `e`, and why did we not
44 // have to do that in our previous version?
45 // 
46 // The answer is already hidden in the type of `vec_min`: `v` is just borrowed, but
47 // the Option<BigInt> that it returns is *owned*. We can't just return one of the elements of `v`,
48 // as that would mean that it is no longer in the vector! In our code, this comes up when we update
49 // the intermediate variable `min`, which also has type `Option<BigInt>`. If you replace get rid of the
50 // `e.clone()`, Rust will complain "Cannot move out of borrowed content". That's because
51 // `e` is a `&BigInt`. Assigning `min = Some(*e)` works just like a function call: Ownership of the
52 // underlying data is transferred from where `e` borrows from to `min`. But that's not allowed, since
53 // we just borrowed `e`, so we cannot empty it! We can, however, call `clone()` on it. Then we own
54 // the copy that was created, and hence we can store it in `min`.<br/>
55 // Of course, making such a full copy is expensive, so we'd like to avoid it. We'll some to that in the next part.
56
57 // ## `Copy` types
58 // But before we go there, I should answer the second question I brought up above: Why did our old `vec_min` work?
59 // We stored the minimal `i32` locally without cloning, and Rust did not complain. That's because there isn't
60 // really much of an "ownership" when it comes to types like `i32` or `bool`: If you move the value from one
61 // place to another, then both instances are "complete". We also say the value has been *duplicated*. This is in
62 // stark contrast to types like `Vec<i32>`, where moving the value results in both the old and the new vector to
63 // point to the same underlying buffer. We don't have two vectors, there's no proper duplication.
64 //
65 // Rust calls types that can be easily duplicated `Copy` types. `Copy` is another trait, and it is implemented for
66 // types like `i32` and `bool`. Remember how we defined the trait `Minimum` by writing `trait Minimum : Copy { ...`?
67 // This tells Rust that every type that implements `Minimum` must also implement `Copy`, and that's why the compiler
68 // accepted our generic `vec_min` in part 02. `Copy` is the first *marker trait* that we encounter: It does not provide
69 // any methods, but makes a promise about the behavior of the type - in this case, being duplicable.
70
71 // If you try to implement `Copy` for `BigInt`, you will notice that Rust
72 // does not let you do that. A type can only be `Copy` if all its elements
73 // are `Copy`, and that's not the case for `BigInt`. However, we can make
74 // `SomethingOrNothing<T>` copy if `T` is `Copy`.
75 use part02::{SomethingOrNothing,Something,Nothing};
76 impl<T: Copy> Copy for SomethingOrNothing<T> {}
77 // Again, Rust can generate implementations of `Copy` automatically. If
78 // you add `#[derive(Copy,Clone)]` right before the definition of `SomethingOrNothing`,
79 // both `Copy` and `Clone` will automatically be implemented.
80
81 // ## An operational perspective
82 // Instead of looking at what happens "at the surface" (i.e., visible in Rust), one can also explain
83 // ownership passing and how `Copy` and `Clone` fit in by looking at what happens on the machine.<br/>
84 // When Rust code is executed, passing a value (like `i32` or `Vec<i32>`) to a function will always
85 // result in a shallow copy being performed: Rust just copies the bytes representing that value, and
86 // considers itself done. That's just like the default copy constructor in C++. Rust, however, will
87 // consider this a destructive operation: After copying the bytes elsewhere, the original value must
88 // no longer be used. After all, the two could now share a pointer! If, however, you mark a type `Copy`,
89 // then Rust will *not* consider a move destructive, and just like in C++, the old and new value
90 // can happily coexist. Now, Rust does not allow you to overload the copy constructor. This means that
91 // passing a value around will always be a fast operation, no allocation or any other kind of heap access
92 // will happen. In the situations where you would write a copy constructor in C++ (and hence
93 // incur a hidden cost on every copy of this type), you'd have the type *not* implement `Copy`, but only
94 // `Clone`. This makes the cost explicit.
95
96 // ## Lifetimes
97 // To fix the performance problems of `vec_min`, we need to avoid using `clone()`. We'd like
98 // the return value to not be owned (remember that this was the source of our need for cloning), but *borrowed*.
99
100 // The function `head` demonstrates how that could work: It borrows the first element of a vector if it is non-empty.
101 // The type of the function says that it will either return nothing, or it will return a borrowed `T`.
102 // We can then borrow the first element of `v` and use it to construct the return value.
103 fn head<T>(v: &Vec<T>) -> Option<&T> {
104     if v.len() > 0 {
105         Some(&v[0])
106     } else {
107         None
108     }
109 }
110 // Technically, we are returning a pointer to the first element. But doesn't that mean that callers have to be
111 // careful? Imagine `head` would be a C++ function, and we would write the following code.
112 /*
113   int foo(std::vector<int> v) {
114     int *first = head(v);
115     v.push_back(42);
116     return *first;
117   }
118 */
119 // This is very much like our very first motivating example for ownership, at the beginning of part 04:
120 // `push_back` could reallocate the buffer, making `first` an invalid pointer. Again, we have aliasing (of `first`
121 // and `v`) and mutation. But this time, the bug is hidden behind the call to `head`. How does Rust solve this? If we translate
122 // the code above to Rust, it doesn't compile, so clearly we are good - but how and why?
123 // (Notice that have to explicitly assert using `unwrap` that `first` is not `None`, whereas the C++ code
124 // above would silently dereference a `NULL`-pointer. But that's another point.)
125 fn rust_foo(mut v: Vec<i32>) -> i32 {
126     let first: Option<&i32> = head(&v);
127     /* v.push(42); */
128     *first.unwrap()
129 }
130
131 // To give the answer to this question, we have to talk about the *lifetime* of a borrow. The point is, saying that
132 // you borrowed your friend a `Vec<i32>`, or a book, is not good enough, unless you also agree on *how long*
133 // your friend can borrow it. After all, you need to know when you can rely on owning your data (or book) again.
134 // 
135 // Every borrow in Rust has an associated lifetime, written `&'a T` for a borrow of type `T` with lifetime `'a`. The full
136 // type of `head` reads as follows: `fn<'a, T>(&'a Vec<T>) -> Option<&'a T>`. Here, `'a` is a *lifetime variable*, which
137 // represents how long the vector has been borrowed. The function type expresses that argument and return value have *the same lifetime*.
138 // 
139 // When analyzing the code of `rust_foo`, Rust has to assign a lifetime to `first`. It will choose the scope
140 // where `first` is valid, which is the entire rest of the function. Because `head` ties the lifetime of its
141 // argument and return value together, this means that `&v` also has to borrow `v` for the entire duration of
142 // the function. So when we try to borrow `v` mutable for `push`, Rust complains that the two borrows (the one
143 // for `head`, and the one for `push`) overlap. Lucky us! Rust caught our mistake and made sure we don't crash the program.
144 // 
145 // So, to sum this up: Lifetimes enable Rust to reason about *how long* a pointer has been borrowed. We can thus
146 // safely write functions like `head`, that return pointers into data they got as argument, and make sure they
147 // are used correctly, *while looking only at the function type*. At no point in our analysis of `rust_foo` did
148 // we have to look *into* `head`. That's, of course, crucial if we want to separate library code from application code.
149 // Most of the time, we don't have to explicitly add lifetimes to function types. This is thanks to *lifetimes elision*,
150 // where Rust will automatically insert lifetimes we did not specify, following some [simple, well-documented rules](http://doc.rust-lang.org/stable/book/lifetimes.html#lifetime-elision).
151
152 // [index](main.html) | [previous](part05.html) | [next](part07.html)