add link to the advanced programming guide
[rust-101.git] / src / part16.rs
1 // Rust-101, Part 16: Unsafe Rust, Drop
2 // ====================================
3
4 use std::ptr;
5 use std::mem;
6 use std::marker::PhantomData;
7
8 //@ As we saw, the rules Rust imposes to ensure memory safety can get us pretty far. A surprising amount of programming patterns
9 //@ can be written within safe Rust, and, more importantly, library code like iterators or threads can make
10 //@ use of the type system to ensure some level of correctness beyond basic memory safety.
11 //@ 
12 //@ However, there will still be programs that one cannot write in accordance with the borrow checker. And there
13 //@ will be cases where it may be possible to satisfy the compiler, but only at the cost of some run-time overhead,
14 //@ as we saw with `RefCell` - overhead which may not be acceptable. In such a situation, it is possible to
15 //@ use *unsafe* Rust: That's a part of the language that is *known* to open the gate to invalid pointer access
16 //@ and all other sorts of memory safety. It is typically disabled, guarded by the keyword `unsafe`. Of course,
17 //@ `unsafe` also means "Here Be Dragons": You are on your own now.
18 //@ 
19 //@ The goal in these cases is to confine unsafety to the local module. Types like `Rc` and `Vec` are implemented
20 //@ using unsafe Rust, but *using* them as we did is (believed to be) perfectly safe.
21 //@ 
22 //@ ## Unsafe Code
23 //@ As an example, let us write a doubly-linked list. Clearly, such a data-structure involves aliasing and mutation:
24 //@ Every node in the list is pointed to by its left and right neighbor, but still we will want to modify the nodes
25 //@ (either to change the value at that place, or to insert/delete nodes). We could now try some clever combination of
26 //@ `Rc` and `RefCell`, but this would end up being quite annoying - and it would incur some over-head. For a low-level
27 //@ data-structure like a doubly-linked list, it makes sense to implement an efficient version *once*, that is unsafe
28 //@ internally, but that can be used without any risk by safe client code.
29
30 //@ As usually, we start by defining the types. Everything is parameterized by the type `T` of the data stored in the list.
31 // A node of the list consists of the data, and two node pointers for the predecessor and successor.
32 struct Node<T> {
33     next: NodePtr<T>,
34     prev: NodePtr<T>,
35     data: T,
36 }
37 // A node pointer is a *mutable raw pointer* to a node.
38 //@ Raw pointers (`*mut T` and `*const T`) are the Rust equivalent of pointers in C. Unlike borrows, they do not come with
39 //@ any guarantees: Raw pointers can be null, or they can point to garbage. They don't have a lifetime, either.
40 type NodePtr<T> = *mut Node<T>;
41
42 // The linked list itself stores pointers to the first and the last node. In addition, we tell Rust that this type
43 // will own data of type `T`.
44 //@ The type `PhantomData<T>` does not actually store anything in memory - it has size zero. However, logically,
45 //@ Rust will consider a `T` to be present. In this case, Rust knows that data of type `T` may be dropped
46 //@ whenever a `LinkedList<T>` is dropped. The checks involving destructors are pretty subtle, so it's always
47 //@ a good idea to provide such extra information. In safe Rust, this can all be done automatically, but here,
48 //@ we just have a `*mut Node<T>`, which Rust does not consider as actually owning the data it points to.
49 pub struct LinkedList<T> {
50     first: NodePtr<T>,
51     last:  NodePtr<T>,
52     _marker: PhantomData<T>,
53 }
54
55 //@ Before we get to the actual linked-list methods, we write two short helper functions converting between
56 //@ mutable raw pointers, and owned pointers (aka `Box`). Both employ `mem::transmute`, which is Rust's
57 //@ `reinterpret_cast`: It can convert anything to anything, by just re-interpreting the bytes. Clearly,
58 //@ that's an unsafe operation and must only be used with great care. If at all possible, its use should be avoided. <br/>
59 //@ We are making the assumption here that a `Box` and a raw pointer have the same representation in memory. In the future,
60 //@ Rust will [provide](http://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#method.from_raw) such [operations](http://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#method.into_raw) in the standard library, but the exact API is still being fleshed out.
61
62 //@ We declare `raw_into_box` to be an `unsafe` function, telling Rust that calling this function is not generally safe.
63 //@ The caller will have to ensure that `r` is a valid pointer, and that nobody else has a pointer to this data.
64 unsafe fn raw_into_box<T>(r: *mut T) -> Box<T> {
65     mem::transmute(r)
66 }
67 //@ The case is slightly different for `box_into_raw`: Converting a `Box` to a raw pointer is always safe. I just drops some
68 //@ information. Hence we keep the function itself safe, and use an *unsafe block* within the function. This is an (unchecked)
69 //@ promise to the Rust compiler, saying that a safe invocation of `box_into_raw` cannot go wrong.
70 fn box_into_raw<T>(b: Box<T>) -> *mut T {
71     unsafe { mem::transmute(b) }
72 }
73
74 impl<T> LinkedList<T> {
75     // A new linked list just contains null pointers. `PhantomData` is how we construct any `PhantomData<T>`.
76     pub fn new() -> Self {
77         LinkedList { first: ptr::null_mut(), last: ptr::null_mut(), _marker: PhantomData }
78     }
79
80     // This function adds a new node to the end of the list.
81     pub fn push_back(&mut self, t: T) {
82         // Create the new node, and make it a raw pointer.
83         //@ Calling `box_into_raw` gives up ownership of the box, which is crucial: We don't want the
84         //@ memory that it points to to be deallocated!
85         let new = Box::new( Node { data: t, next: ptr::null_mut(), prev: self.last } );
86         let new = box_into_raw(new);
87         // Update other points to this node.
88         if self.last.is_null() {
89             debug_assert!(self.first.is_null());
90             // The list is currently empty, so we have to update the head pointer.
91             self.first = new;                                       /*@*/
92         } else {
93             debug_assert!(!self.first.is_null());
94             // We have to update the `next` pointer of the tail node.
95             //@ Since Rust does not know that a raw pointer actually points to anything, dereferencing such a pointer is
96             //@ an unsafe operation. So this unsafe block promises that the pointer will actually be valid.
97             unsafe { (*self.last).next = new; }                     /*@*/
98         }
99         // Make this the last node.
100         self.last = new;
101     }
102
103     // **Exercise 16.1**: Add some more operations to `LinkedList`: `pop_back`, `push_front` and `pop_front`.
104     // Add testcases for `push_back` and all of your functions. The `pop` functions should take `&mut self`
105     // and return `Option<T>`.
106
107     // Next, we are going to provide an iterator.
108     //@ This function just creates an instance of `IterMut`, the iterator type which does the actual work.
109     pub fn iter_mut(&self) -> IterMut<T> {
110         IterMut { next: self.first, _marker: PhantomData  }
111     }
112 }
113
114 //@ What does the iterator need to store? Strictly speaking, all it needs is the pointer to the next node
115 //@ that it is going to visit. However, how do we make sure that this pointer remains valid? We have to
116 //@ get this right ourselves, as we left the safe realms of borrowing and ownership. Remember that the
117 //@ key ingredient for iterator safety was to tie the lifetime of the iterator to the lifetime of the
118 //@ borrow used for `iter_mut`. We will thus give `IterMut` two parameters: A type parameter specifying
119 //@ the type of the data, and a lifetime parameter specifying for how long the list was borrowed to the
120 //@ iterator.
121
122 //@ For Rust to accept the type, we have to add two more annotations. First of all, we have to ensure that
123 //@ the data in the list lives at least as long as the iterator: If you drop the `T: 'a`, Rust will tell
124 //@ you to add it back. And secondly, Rust will complain if `'a` is not actually used in the struct.
125 //@ It doesn't know what it is supposed to do with that lifetime. So we use `PhantomData` again to tell
126 //@ it that in terms of ownership, this type actually (mutably) borrows a linked list. This has no
127 //@ operational effect, but it means that Rust can deduce the intent we had when adding that
128 //@ seemingly useless lifetime parameter.
129 pub struct IterMut<'a, T> where T: 'a {
130     next: NodePtr<T>,
131     _marker: PhantomData<&'a mut LinkedList<T>>,
132 }
133
134 //@ When implementing `Iterator` for `IterMut`, the fact that we have the lifetime `'a` around immediately
135 //@ pays of: We would not even be able to write down the type `Item` without that lifetime.
136 impl<'a, T> Iterator for IterMut<'a, T> {
137     type Item = &'a mut T;
138
139     fn next(&mut self) -> Option<Self::Item> {
140         // The actual iteration is straight-forward: Once we reached a null pointer, we are done.
141         if self.next.is_null() {
142             None
143         } else {
144             // Otherwise, we can convert the next pointer to a borrow, get a borrow to the data
145             // and update the iterator.
146             let next = unsafe { &mut *self.next };
147             let ret = &mut next.data;
148             self.next = next.next;                                  /*@*/
149             Some(ret)                                               /*@*/
150         }
151     }
152 }
153
154 //@ In `next` above, we made crucial use of the assumption that `self.next` is either null or a valid pointer.
155 //@ This only works because if someone tries to delete elements from a list during iteration, we know that the borrow checker
156 //@ will catch them: If they call `next`, the lifetime `'a` we artificially added to the iterator has to still be
157 //@ active, which means the mutable borrow passed to `iter_mut` is still active, which means nobody can delete
158 //@ anything from the list. In other words, we make use of the expressive type system of Rust, decorating
159 //@ our own unsafe implementation with just enough information so that Rust can check *uses* of the linked-list.
160 //@ If the type system were weaker, we could not write a linked-list like the above with a safe interface!
161
162 // **Exercise 16.2**: Add a method `iter` and a type `Iter` providing iteration for shared borrows.
163 // Add testcases for both kinds of iterators.
164
165 // ## `Drop`
166 //@ The linked list we wrote is already working quite nicely, but there is one problem: When the list is dropped,
167 //@ nobody bothers to deallocate the remaining nodes. Even worse, if `T` itself has a destructor that needs to
168 //@ clean up, it is not called for the element remaining in the list. We need to take care of that ourselves.
169
170 //@ In Rust, adding a destructor for a type is done by implementing the `Drop` trait. This is a very special trait.
171 //@ It can only be implemented for *nominal types*, i.e., you cannot implement `Drop` for `&mut T`. You also cannot
172 //@ restrict the type and lifetime parameters further than the type does - the `Drop` implementation has to apply to *all* instances
173 //@ of `LinkedList`.
174 impl<T> Drop for LinkedList<T> {
175     // The destructor itself is a method which takes `self` in mutably borrowed form. It cannot own `self`, because then
176     // the destructor of `self` would be called at the end pf the function, resulting in endless recursion...
177     fn drop(&mut self) {
178         let mut cur_ptr = self.first;
179         while !cur_ptr.is_null() {
180             // In the destructor, we just iterate over the entire list, successively obtaining ownership
181             // (`Box`) of every node. When the box is dropped, it will call the destructor on `data` if
182             // necessary, and subsequently free the node on the heap.
183             //@ We call `drop` explicitly here just for documentation purposes.
184             let cur = unsafe { raw_into_box(cur_ptr) };
185             cur_ptr = cur.next;
186             drop(cur);
187         }
188     }
189 }
190
191 // ## The End
192 //@ Congratulations! You completed Rust-101. This was the last part of the course. I hope you enjoyed it.
193 //@ If you have feedback or want to contribute yourself, please head to the [Rust-101](https://www.ralfj.de/projects/rust-101/) website
194 //@ fur further information. The entire course is open-source (under [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)).
195 //@ 
196 //@ If you want to do more, the examples you saw in this course provide lots of playground for coming up with your own little
197 //@ extensions here and there. The [index](main.html) contains some more links to additional resources you may find useful. 
198 //@ With that, there's only one thing left to say: Happy Rust Hacking!
199
200 //@ [index](main.html) | [previous](part15.html) | next