X-Git-Url: https://git.ralfj.de/rust-101.git/blobdiff_plain/67c07b22fc19f6e39b41bdd5c95b02643f51e81b..a845c41ee688b4a32c196d847a3a94349736cfd8:/src/part16.rs?ds=sidebyside diff --git a/src/part16.rs b/src/part16.rs index f5735b8..efa48a5 100644 --- a/src/part16.rs +++ b/src/part16.rs @@ -32,7 +32,7 @@ struct Node { data: T, } // A node pointer is a *mutable raw pointer* to a node. -//@ Raw pointers (`*mut T` and `*const T`) are the Rust equivalent of pointers in C. Unlike borrows, they do not come with +//@ Raw pointers (`*mut T` and `*const T`) are the Rust equivalent of pointers in C. Unlike references, they do not come with //@ any guarantees: Raw pointers can be null, or they can point to garbage. They don't have a lifetime, either. type NodePtr = *mut Node; @@ -55,12 +55,12 @@ pub struct LinkedList { //@ Clearly, that's an unsafe operation and must only be used with great care - or even better, not at all. Seriously. //@ If at all possible, you should never use `transmute`.
//@ We are making the assumption here that a `Box` and a raw pointer have the same representation in memory. In the future, -//@ 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. +//@ Rust will [provide](https://doc.rust-lang.org/beta/alloc/boxed/struct.Box.html#method.from_raw) such [operations](https://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. //@ We declare `raw_into_box` to be an `unsafe` function, telling Rust that calling this function is not generally safe. //@ This grants us the unsafe powers for the body of the function: We can dereference raw pointers, and - most importantly - we -//@ can call unsafe functions. (There's a third power, related to mutable static variables, but we didn't talk about static variables -//@ in the course, so that won't be relevant here.)
+//@ can call unsafe functions. (The other unsafe powers won't be relevant here. Go read [The Rustonomicon](https://doc.rust-lang.org/nightly/nomicon/) +//@ if you want to learn all about this, but be warned - That Way Lies Madness.)
//@ Here, the caller will have to ensure that `r` is a valid pointer, and that nobody else has a pointer to this data. unsafe fn raw_into_box(r: *mut T) -> Box { mem::transmute(r) @@ -84,7 +84,7 @@ impl LinkedList { //@ Calling `box_into_raw` gives up ownership of the box, which is crucial: We don't want the memory that it points to to be deallocated! let new = Box::new( Node { data: t, next: ptr::null_mut(), prev: self.last } ); let new = box_into_raw(new); - // Update other points to this node. + // Update other pointers to this node. if self.last.is_null() { debug_assert!(self.first.is_null()); // The list is currently empty, so we have to update the head pointer. @@ -106,7 +106,7 @@ impl LinkedList { // Next, we are going to provide an iterator. //@ This function just creates an instance of `IterMut`, the iterator type which does the actual work. - pub fn iter_mut(&self) -> IterMut { + pub fn iter_mut(&mut self) -> IterMut { IterMut { next: self.first, _marker: PhantomData } } } @@ -115,7 +115,7 @@ impl LinkedList { //@ that it is going to visit. However, how do we make sure that this pointer remains valid? We have to //@ get this right ourselves, as we left the safe realms of borrowing and ownership. Remember that the //@ key ingredient for iterator safety was to tie the lifetime of the iterator to the lifetime of the -//@ borrow used for `iter_mut`. We will thus give `IterMut` two parameters: A type parameter specifying +//@ reference used for `iter_mut`. We will thus give `IterMut` two parameters: A type parameter specifying //@ the type of the data, and a lifetime parameter specifying for how long the list was borrowed to the //@ iterator. @@ -123,7 +123,7 @@ impl LinkedList { //@ the data in the list lives at least as long as the iterator: If you drop the `T: 'a`, Rust will tell //@ you to add it back. And secondly, Rust will complain if `'a` is not actually used in the struct. //@ It doesn't know what it is supposed to do with that lifetime. So we use `PhantomData` again to tell -//@ it that in terms of ownership, this type actually (mutably) borrows a linked list. This has no +//@ it that in terms of ownership, this type actually (uniquely) borrows a linked list. This has no //@ operational effect, but it means that Rust can deduce the intent we had when adding that //@ seemingly useless lifetime parameter. pub struct IterMut<'a, T> where T: 'a { @@ -141,7 +141,7 @@ impl<'a, T> Iterator for IterMut<'a, T> { if self.next.is_null() { None } else { - // Otherwise, we can convert the next pointer to a borrow, get a borrow to the data + // Otherwise, we can convert the next pointer to a reference, get a reference to the data // and update the iterator. let next = unsafe { &mut *self.next }; let ret = &mut next.data; @@ -154,12 +154,12 @@ impl<'a, T> Iterator for IterMut<'a, T> { //@ In `next` above, we made crucial use of the assumption that `self.next` is either null or a valid pointer. //@ This only works because if someone tries to delete elements from a list during iteration, we know that the borrow checker //@ will catch them: If they call `next`, the lifetime `'a` we artificially added to the iterator has to still be -//@ active, which means the mutable borrow passed to `iter_mut` is still active, which means nobody can delete +//@ active, which means the mutable reference passed to `iter_mut` is still active, which means nobody can delete //@ anything from the list. In other words, we make use of the expressive type system of Rust, decorating //@ our own unsafe implementation with just enough information so that Rust can check *uses* of the linked-list. //@ If the type system were weaker, we could not write a linked-list like the above with a safe interface! -// **Exercise 16.2**: Add a method `iter` and a type `Iter` providing iteration for shared borrows. +// **Exercise 16.2**: Add a method `iter` and a type `Iter` providing iteration for shared references. // Add testcases for both kinds of iterators. // ## `Drop` @@ -173,7 +173,7 @@ impl<'a, T> Iterator for IterMut<'a, T> { //@ of `LinkedList`. impl Drop for LinkedList { // The destructor itself is a method which takes `self` in mutably borrowed form. It cannot own `self`, because then - // the destructor of `self` would be called at the end pf the function, resulting in endless recursion... + // the destructor of `self` would be called at the end of the function, resulting in endless recursion. fn drop(&mut self) { let mut cur_ptr = self.first; while !cur_ptr.is_null() { @@ -197,4 +197,4 @@ impl Drop for LinkedList { //@ extensions here and there. The [index](main.html) contains some more links to additional resources you may find useful. //@ With that, there's only one thing left to say: Happy Rust Hacking! -//@ [index](main.html) | [previous](part15.html) | next +//@ [index](main.html) | [previous](part15.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part16.rs) | next