Skip to content

Commit d15a0b5

Browse files
committed
WIP most of sixth
1 parent 906cbe2 commit d15a0b5

12 files changed

+2301
-12
lines changed

src/SUMMARY.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,15 @@
4444
* [Final Code](fifth-final.md)
4545
* [A Production Unsafe Deque](sixth.md)
4646
* [Layout](sixth-layout.md)
47-
* [Unsafe](sixth-basics.md)
47+
* [Variance and Subtyping](sixth-variance.md)
48+
* [Basics](sixth-basics.md)
49+
* [Panic Safety](sixth-panics.md)
50+
* [Boring Combinatorics](sixth-combinatorics.md)
51+
* [Filling In Random Bits](sixth-random-bits.md)
52+
* [Testing](sixth-testing.md)
53+
* [Send, Sync, and Compile Tests](sixth-send-sync.md)
54+
* [Cursors](sixth-cursors.md)
55+
* [Final Code](sixth-final.md)
4856
* [A Bunch of Silly Lists](infinity.md)
4957
* [The Double Single](infinity-double-single.md)
5058
* [The Stack-Allocated Linked List](infinity-stack-allocated.md)

src/sixth-basics.md

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# Basics
2+
3+
Alright, this is the part of the book that sucks, and why it took me 7 years to write this chapter! Time to just burn through a whole lot of really boring stuff we've done 5 times already, but extra verbose and long because we have to do everything twice and with `Option<NonNull<Node<T>>>`!
4+
5+
```rust ,ignore
6+
impl<T> LinkedList<T> {
7+
pub fn new() -> Self {
8+
Self {
9+
front: None,
10+
back: None,
11+
len: 0,
12+
_boo: PhantomData,
13+
}
14+
}
15+
}
16+
```
17+
18+
PhantomData is a weird type with no fields so you just make one by, saying its type name. *shrug*
19+
20+
```rust ,ignore
21+
pub fn push_front(&mut self, elem: T) {
22+
// SAFETY: it's a linked-list, what do you want?
23+
unsafe {
24+
let new = NonNull::new_unchecked(Box::into_raw(Box::new(Node {
25+
front: None,
26+
back: None,
27+
elem,
28+
})));
29+
if let Some(old) = self.front {
30+
// Put the new front before the old one
31+
(*old).front = Some(new);
32+
(*new).back = Some(old);
33+
} else {
34+
// If there's no front, then we're the empty list and need
35+
// to set the back too. Also here's some integrity checks
36+
// for testing, in case we mess up.
37+
debug_assert!(self.back.is_none());
38+
debug_assert!(self.front.is_none());
39+
debug_assert!(self.len == 0);
40+
self.back = Some(new);
41+
}
42+
self.front = Some(new);
43+
self.len += 1;
44+
}
45+
}
46+
```
47+
48+
```text
49+
error[E0614]: type `NonNull<Node<T>>` cannot be dereferenced
50+
--> src\lib.rs:39:17
51+
|
52+
39 | (*old).front = Some(new);
53+
| ^^^^^^
54+
```
55+
56+
57+
Ah yes, I truly hate my pointer-y children. We need to explicitly get the raw pointer out of NonNull with `as_ptr`, because DerefMut is defined in terms of `&mut` and we don't want to randomly introduce safe references into our unsafe code!
58+
59+
60+
```rust ,ignore
61+
(*old.as_ptr()).front = Some(new);
62+
(*new.as_ptr()).back = Some(old);
63+
```
64+
65+
```text
66+
Compiling linked-list v0.0.3
67+
warning: field is never read: `elem`
68+
--> src\lib.rs:16:5
69+
|
70+
16 | elem: T,
71+
| ^^^^^^^
72+
|
73+
= note: `#[warn(dead_code)]` on by default
74+
75+
warning: `linked-list` (lib) generated 1 warning (1 duplicate)
76+
warning: `linked-list` (lib test) generated 1 warning
77+
Finished test [unoptimized + debuginfo] target(s) in 0.33s
78+
```
79+
80+
Nice, now for pop (and len):
81+
82+
```rust ,ignore
83+
pub fn pop_front(&mut self) -> Option<T> {
84+
unsafe {
85+
// Only have to do stuff if there is a front node to pop.
86+
// Note that we don't need to mess around with `take` anymore
87+
// because everything is Copy and there are no dtors that will
88+
// run if we mess up... right? :) Riiiight? :)))
89+
self.front.map(|node| {
90+
// Bring the Box back to life so we can move out its value and
91+
// Drop it (Box continues to magically understand this for us).
92+
let boxed_node = Box::from_raw(node.as_ptr());
93+
let result = boxed_node.elem;
94+
95+
// Make the next node into the new front.
96+
self.front = boxed_node.back;
97+
if let Some(new) = self.front {
98+
// Cleanup its reference to the removed node
99+
(*new.as_ptr()).front = None;
100+
} else {
101+
// If the front is now null, then this list is now empty!
102+
debug_assert!(self.len == 1);
103+
self.back = None;
104+
}
105+
106+
self.len -= 1;
107+
result
108+
// Box gets implicitly freed here, knows there is no T.
109+
})
110+
}
111+
}
112+
113+
pub fn len(&self) -> usize {
114+
self.len
115+
}
116+
```
117+
118+
```text
119+
Compiling linked-list v0.0.3
120+
Finished dev [unoptimized + debuginfo] target(s) in 0.37s
121+
```
122+
123+
Seems legit to me, time to write a test!
124+
125+
```rust ,ignore
126+
#[cfg(test)]
127+
mod test {
128+
use super::LinkedList;
129+
130+
#[test]
131+
fn test_basic_front() {
132+
let mut list = LinkedList::new();
133+
134+
// Try to break an empty list
135+
assert_eq!(list.len(), 0);
136+
assert_eq!(list.pop_front(), None);
137+
assert_eq!(list.len(), 0);
138+
139+
// Try to break a one item list
140+
list.push_front(10);
141+
assert_eq!(list.len(), 1);
142+
assert_eq!(list.pop_front(), Some(10));
143+
assert_eq!(list.len(), 0);
144+
assert_eq!(list.pop_front(), None);
145+
assert_eq!(list.len(), 0);
146+
147+
// Mess around
148+
list.push_front(10);
149+
assert_eq!(list.len(), 1);
150+
list.push_front(20);
151+
assert_eq!(list.len(), 2);
152+
list.push_front(30);
153+
assert_eq!(list.len(), 3);
154+
assert_eq!(list.pop_front(), Some(30));
155+
assert_eq!(list.len(), 2);
156+
list.push_front(40);
157+
assert_eq!(list.len(), 3);
158+
assert_eq!(list.pop_front(), Some(40));
159+
assert_eq!(list.len(), 2);
160+
assert_eq!(list.pop_front(), Some(20));
161+
assert_eq!(list.len(), 1);
162+
assert_eq!(list.pop_front(), Some(10));
163+
assert_eq!(list.len(), 0);
164+
assert_eq!(list.pop_front(), None);
165+
assert_eq!(list.len(), 0);
166+
assert_eq!(list.pop_front(), None);
167+
assert_eq!(list.len(), 0);
168+
}
169+
}
170+
```
171+
172+
173+
```text
174+
Compiling linked-list v0.0.3
175+
Finished test [unoptimized + debuginfo] target(s) in 0.40s
176+
Running unittests src\lib.rs
177+
178+
running 1 test
179+
test test::test_basic_front ... ok
180+
181+
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
182+
```
183+
184+
Hooray, we're perfect!
185+
186+
...Right?

0 commit comments

Comments
 (0)