From 5e789618d9210fe4e67b19e25cfba5c3c1fde35d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 30 Jun 2021 06:19:17 +0900 Subject: [PATCH] Audit `ignore` annotations --- src/aliasing.md | 1 + src/arc-mutex/arc-base.md | 10 ++++++++++ src/arc-mutex/arc-clone.md | 8 +++++++- src/arc-mutex/arc-drop.md | 14 +++++++------- src/arc-mutex/arc-layout.md | 10 +++++----- src/atomics.md | 6 ++++-- src/destructors.md | 1 + src/dropck.md | 12 +++++++++--- src/exception-safety.md | 3 ++- src/exotic-sizes.md | 2 +- src/ffi.md | 15 +++++++++++++-- src/hrtb.md | 2 ++ src/leaking.md | 5 +++++ src/lifetime-elision.md | 3 ++- src/lifetime-mismatch.md | 1 + src/lifetimes.md | 5 +++++ src/ownership.md | 4 ++-- src/panic-handler.md | 17 +++++++++++------ src/repr-rust.md | 1 + src/subtyping.md | 11 ++++++++++- src/unbounded-lifetimes.md | 1 + src/unchecked-uninit.md | 1 + src/vec/vec-alloc.md | 3 +++ src/vec/vec-dealloc.md | 1 + src/vec/vec-deref.md | 2 ++ src/vec/vec-drain.md | 4 ++++ src/vec/vec-insert-remove.md | 2 ++ src/vec/vec-into-iter.md | 5 +++++ src/vec/vec-layout.md | 1 + src/vec/vec-push-pop.md | 2 ++ src/vec/vec-raw.md | 3 +++ src/vec/vec-zsts.md | 3 +++ src/working-with-unsafe.md | 1 + 33 files changed, 128 insertions(+), 32 deletions(-) diff --git a/src/aliasing.md b/src/aliasing.md index 7f6e830..993afba 100644 --- a/src/aliasing.md +++ b/src/aliasing.md @@ -56,6 +56,7 @@ and `output` overlap, such as `compute(&x, &mut x)`. With that input, we could get this execution: + ```rust,ignore // input == output == 0xabad1dea // *input == *output == 20 diff --git a/src/arc-mutex/arc-base.md b/src/arc-mutex/arc-base.md index 4af9884..ee35d86 100644 --- a/src/arc-mutex/arc-base.md +++ b/src/arc-mutex/arc-base.md @@ -10,6 +10,7 @@ We'll first need a way to construct an `Arc`. This is pretty simple, as we just need to box the `ArcInner` and get a `NonNull` pointer to it. + ```rust,ignore impl Arc { pub fn new(data: T) -> Arc { @@ -41,6 +42,7 @@ This is okay because: if it is the only `Arc` referencing that data (which only happens in `Drop`) * We use atomics for the shared mutable reference counting + ```rust,ignore unsafe impl Send for Arc {} unsafe impl Sync for Arc {} @@ -61,6 +63,8 @@ as `Rc` is not thread-safe. To dereference the `NonNull` pointer into a `&T`, we can call `NonNull::as_ref`. This is unsafe, unlike the typical `as_ref` function, so we must call it like this: + + ```rust,ignore unsafe { self.ptr.as_ref() } ``` @@ -79,11 +83,15 @@ to the data inside? What we need now is an implementation of `Deref`. We'll need to import the trait: + + ```rust,ignore use std::ops::Deref; ``` And here's the implementation: + + ```rust,ignore impl Deref for Arc { type Target = T; @@ -101,6 +109,8 @@ Pretty simple, eh? This simply dereferences the `NonNull` pointer to the ## Code Here's all the code from this section: + + ```rust,ignore use std::ops::Deref; diff --git a/src/arc-mutex/arc-clone.md b/src/arc-mutex/arc-clone.md index 960cd16..1adc6c9 100644 --- a/src/arc-mutex/arc-clone.md +++ b/src/arc-mutex/arc-clone.md @@ -9,12 +9,14 @@ Basically, we need to: First, we need to get access to the `ArcInner`: + ```rust,ignore let inner = unsafe { self.ptr.as_ref() }; ``` We can update the atomic reference count as follows: + ```rust,ignore let old_rc = inner.rc.fetch_add(1, Ordering::???); ``` @@ -30,13 +32,14 @@ ordering, see [the section on atomics](../atomics.md). Thus, the code becomes this: + ```rust,ignore let old_rc = inner.rc.fetch_add(1, Ordering::Relaxed); ``` We'll need to add another import to use `Ordering`: -```rust,ignore +```rust use std::sync::atomic::Ordering; ``` @@ -61,6 +64,7 @@ machines) incrementing the reference count at once. This is what we'll do. It's pretty simple to implement this behavior: + ```rust,ignore if old_rc >= isize::MAX as usize { std::process::abort(); @@ -69,6 +73,7 @@ if old_rc >= isize::MAX as usize { Then, we need to return a new instance of the `Arc`: + ```rust,ignore Self { ptr: self.ptr, @@ -78,6 +83,7 @@ Self { Now, let's wrap this all up inside the `Clone` implementation: + ```rust,ignore use std::sync::atomic::Ordering; diff --git a/src/arc-mutex/arc-drop.md b/src/arc-mutex/arc-drop.md index ccbb57c..4ed371f 100644 --- a/src/arc-mutex/arc-drop.md +++ b/src/arc-mutex/arc-drop.md @@ -15,6 +15,7 @@ Basically, we need to: First, we'll need to get access to the `ArcInner`: + ```rust,ignore let inner = unsafe { self.ptr.as_ref() }; ``` @@ -24,6 +25,7 @@ also return if the returned value from `fetch_sub` (the value of the reference count before decrementing it) is not equal to `1` (which happens when we are not the last reference to the data). + ```rust,ignore if inner.rc.fetch_sub(1, Ordering::Relaxed) != 1 { return; @@ -63,20 +65,17 @@ implementation of `Arc`][3]: To do this, we do the following: -```rust,ignore -atomic::fence(Ordering::Acquire); -``` - -We'll need to import `std::sync::atomic` itself: - -```rust,ignore +```rust +# use std::sync::atomic::Ordering; use std::sync::atomic; +atomic::fence(Ordering::Acquire); ``` Finally, we can drop the data itself. We use `Box::from_raw` to drop the boxed `ArcInner` and its data. This takes a `*mut T` and not a `NonNull`, so we must convert using `NonNull::as_ptr`. + ```rust,ignore unsafe { Box::from_raw(self.ptr.as_ptr()); } ``` @@ -86,6 +85,7 @@ pointer is valid. Now, let's wrap this all up inside the `Drop` implementation: + ```rust,ignore impl Drop for Arc { fn drop(&mut self) { diff --git a/src/arc-mutex/arc-layout.md b/src/arc-mutex/arc-layout.md index abab016..3dae2cd 100644 --- a/src/arc-mutex/arc-layout.md +++ b/src/arc-mutex/arc-layout.md @@ -22,7 +22,7 @@ same allocation. Naively, it would look something like this: -```rust,ignore +```rust use std::sync::atomic; pub struct Arc { @@ -31,7 +31,7 @@ pub struct Arc { pub struct ArcInner { rc: atomic::AtomicUsize, - data: T + data: T, } ``` @@ -56,18 +56,18 @@ ownership of a value of `ArcInner` (which itself contains some `T`). With these changes we get our final structure: -```rust,ignore +```rust use std::marker::PhantomData; use std::ptr::NonNull; use std::sync::atomic::AtomicUsize; pub struct Arc { ptr: NonNull>, - phantom: PhantomData> + phantom: PhantomData>, } pub struct ArcInner { rc: AtomicUsize, - data: T + data: T, } ``` diff --git a/src/atomics.md b/src/atomics.md index c663f98..6aef6ae 100644 --- a/src/atomics.md +++ b/src/atomics.md @@ -27,16 +27,18 @@ exactly what we said but, you know, fast. Wouldn't that be great? Compilers fundamentally want to be able to do all sorts of complicated transformations to reduce data dependencies and eliminate dead code. In particular, they may radically change the actual order of events, or make events -never occur! If we write something like +never occur! If we write something like: + ```rust,ignore x = 1; y = 3; x = 2; ``` -The compiler may conclude that it would be best if your program did +The compiler may conclude that it would be best if your program did: + ```rust,ignore x = 2; y = 3; diff --git a/src/destructors.md b/src/destructors.md index 524c401..3d99961 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -3,6 +3,7 @@ What the language *does* provide is full-blown automatic destructors through the `Drop` trait, which provides the following method: + ```rust,ignore fn drop(&mut self); ``` diff --git a/src/dropck.md b/src/dropck.md index 4d2080c..28b65c1 100644 --- a/src/dropck.md +++ b/src/dropck.md @@ -8,11 +8,15 @@ when we talked about `'a: 'b`, it was ok for `'a` to live _exactly_ as long as gets dropped at the same time as another, right? This is why we used the following desugaring of `let` statements: + ```rust,ignore let x; let y; ``` +desugaring to: + + ```rust,ignore { let x; @@ -29,6 +33,7 @@ definition. There are some more details about order of drop in [RFC 1857][rfc185 Let's do this: + ```rust,ignore let tuple = (vec![], vec![]); ``` @@ -259,7 +264,8 @@ lifetime `'b` and that the only uses of `T` will be moves or drops, but omit the attribute from `'a` and `U`, because we do access data with that lifetime and that type: -```rust,ignore +```rust +#![feature(dropck_eyepatch)] use std::fmt::Display; struct Inspector<'a, 'b, T, U: Display>(&'a u8, &'b u8, T, U); @@ -283,7 +289,7 @@ other avenues for such indirect access.) Here is an example of invoking a callback: -```rust,ignore +```rust struct Inspector(T, &'static str, Box fn(&'r T) -> String>); impl Drop for Inspector { @@ -297,7 +303,7 @@ impl Drop for Inspector { Here is an example of a trait method call: -```rust,ignore +```rust use std::fmt; struct Inspector(T, &'static str); diff --git a/src/exception-safety.md b/src/exception-safety.md index e0dd926..3f92e64 100644 --- a/src/exception-safety.md +++ b/src/exception-safety.md @@ -35,6 +35,7 @@ needs to be careful and consider exception safety. `Vec::push_all` is a temporary hack to get extending a Vec by a slice reliably efficient without specialization. Here's a simple implementation: + ```rust,ignore impl Vec { fn push_all(&mut self, to_push: &[T]) { @@ -75,7 +76,6 @@ bubble_up(heap, index): while index != 0 && heap[index] < heap[parent(index)]: heap.swap(index, parent(index)) index = parent(index) - ``` A literal transcription of this code to Rust is totally fine, but has an annoying @@ -147,6 +147,7 @@ way to do this is to store the algorithm's state in a separate struct with a destructor for the "finally" logic. Whether we panic or not, that destructor will run and clean up after us. + ```rust,ignore struct Hole<'a, T: 'a> { data: &'a mut [T], diff --git a/src/exotic-sizes.md b/src/exotic-sizes.md index 4357d80..8ada0fb 100644 --- a/src/exotic-sizes.md +++ b/src/exotic-sizes.md @@ -139,7 +139,7 @@ other is still UB). The following *could* also compile: -```rust,ignore +```rust,compile_fail enum Void {} let res: Result = Ok(0); diff --git a/src/ffi.md b/src/ffi.md index 7ea2530..8012bbc 100644 --- a/src/ffi.md +++ b/src/ffi.md @@ -28,6 +28,7 @@ and add `extern crate libc;` to your crate root. The following is a minimal example of calling a foreign function which will compile if snappy is installed: + ```rust,ignore extern crate libc; use libc::size_t; @@ -61,6 +62,7 @@ of keeping the binding correct at runtime. The `extern` block can be extended to cover the entire snappy API: + ```rust,ignore extern crate libc; use libc::{c_int, size_t}; @@ -96,6 +98,7 @@ vectors as pointers to memory. Rust's vectors are guaranteed to be a contiguous length is the number of elements currently contained, and the capacity is the total size in elements of the allocated memory. The length is less than or equal to the capacity. + ```rust,ignore # extern crate libc; # use libc::{c_int, size_t}; @@ -120,6 +123,7 @@ required capacity to hold the compressed output. The vector can then be passed t `snappy_compress` function as an output parameter. An output parameter is also passed to retrieve the true length after compression for setting the length. + ```rust,ignore # extern crate libc; # use libc::{size_t, c_int}; @@ -146,6 +150,7 @@ pub fn compress(src: &[u8]) -> Vec { Decompression is similar, because snappy stores the uncompressed size as part of the compression format and `snappy_uncompressed_length` will retrieve the exact buffer size required. + ```rust,ignore # extern crate libc; # use libc::{size_t, c_int}; @@ -180,6 +185,7 @@ pub fn uncompress(src: &[u8]) -> Option> { Then, we can add some tests to show how to use them. + ```rust,ignore # extern crate libc; # use libc::{c_int, size_t}; @@ -452,6 +458,7 @@ Foreign APIs often export a global variable which could do something like track global state. In order to access these variables, you declare them in `extern` blocks with the `static` keyword: + ```rust,ignore extern crate libc; @@ -470,6 +477,7 @@ Alternatively, you may need to alter global state provided by a foreign interface. To do this, statics can be declared with `mut` so we can mutate them. + ```rust,ignore extern crate libc; @@ -502,6 +510,7 @@ Most foreign code exposes a C ABI, and Rust uses the platform's C calling conven calling foreign functions. Some foreign functions, most notably the Windows API, use other calling conventions. Rust provides a way to tell the compiler which convention to use: + ```rust,ignore extern crate libc; @@ -582,10 +591,10 @@ fn main() { Normal Rust functions can *not* be variadic: -```ignore +```rust,compile_fail // This will not compile -fn foo(x: i32, ...) { } +fn foo(x: i32, ...) {} ``` ## The "nullable pointer optimization" @@ -613,6 +622,7 @@ callback, which gets called in certain situations. The callback is passed a func and an integer and it is supposed to run the function with the integer as a parameter. So we have function pointers flying across the FFI boundary in both directions. + ```rust,ignore extern crate libc; use libc::c_int; @@ -712,6 +722,7 @@ void bar(void *arg); We can represent this in Rust with the `c_void` type: + ```rust,ignore extern crate libc; diff --git a/src/hrtb.md b/src/hrtb.md index 645986a..be5a55b 100644 --- a/src/hrtb.md +++ b/src/hrtb.md @@ -28,6 +28,7 @@ fn main() { If we try to naively desugar this code in the same way that we did in the lifetimes section, we run into some trouble: + ```rust,ignore struct Closure { data: (u8, u16), @@ -60,6 +61,7 @@ named until we enter the body of `call`! Also, that isn't some fixed lifetime; This job requires The Magic of Higher-Rank Trait Bounds (HRTBs). The way we desugar this is as follows: + ```rust,ignore where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8, ``` diff --git a/src/leaking.md b/src/leaking.md index 42684e5..e46be7d 100644 --- a/src/leaking.md +++ b/src/leaking.md @@ -69,6 +69,7 @@ unwinding-safe! Easy! Now consider the following: + ```rust,ignore let mut vec = vec![Box::new(0); 4]; @@ -116,6 +117,7 @@ Nope. Let's consider a simplified implementation of Rc: + ```rust,ignore struct Rc { ptr: *mut RcBox, @@ -179,6 +181,7 @@ data on their parent's stack without any synchronization over that data by ensuring the parent joins the thread before any of the shared data goes out of scope. + ```rust,ignore pub fn scoped<'a, F>(f: F) -> JoinGuard<'a> where F: FnOnce() + Send + 'a @@ -196,6 +199,7 @@ of the closed-over data goes out of scope in the parent. Usage looked like: + ```rust,ignore let mut data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; { @@ -224,6 +228,7 @@ let mut data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; In principle, this totally works! Rust's ownership system perfectly ensures it! ...except it relies on a destructor being called to be safe. + ```rust,ignore let mut data = Box::new(0); { diff --git a/src/lifetime-elision.md b/src/lifetime-elision.md index 7628351..c65a157 100644 --- a/src/lifetime-elision.md +++ b/src/lifetime-elision.md @@ -5,6 +5,7 @@ In order to make common patterns more ergonomic, Rust allows lifetimes to be A *lifetime position* is anywhere you can write a lifetime in a type: + ```rust,ignore &'a T &'a mut T @@ -38,6 +39,7 @@ Elision rules are as follows: Examples: + ```rust,ignore fn print(s: &str); // elided fn print<'a>(s: &'a str); // expanded @@ -60,5 +62,4 @@ fn args<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // ex fn new(buf: &mut [u8]) -> BufWriter; // elided fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded - ``` diff --git a/src/lifetime-mismatch.md b/src/lifetime-mismatch.md index eeac382..18082a4 100644 --- a/src/lifetime-mismatch.md +++ b/src/lifetime-mismatch.md @@ -40,6 +40,7 @@ What happened? Well, we got the exact same reasoning as we did for [Example 2 in the previous section][ex2]. We desugar the program and we get the following: + ```rust,ignore struct Foo; diff --git a/src/lifetimes.md b/src/lifetimes.md index 4d922a1..0a68a28 100644 --- a/src/lifetimes.md +++ b/src/lifetimes.md @@ -45,6 +45,7 @@ let z = &y; The borrow checker always tries to minimize the extent of a lifetime, so it will likely desugar to the following: + ```rust,ignore // NOTE: `'a: {` and `&'b x` is not valid syntax! 'a: { @@ -72,6 +73,7 @@ let y = &x; z = y; ``` + ```rust,ignore 'a: { let x: i32 = 0; @@ -100,6 +102,7 @@ fn as_str(data: &u32) -> &str { desugars to: + ```rust,ignore fn as_str<'a>(data: &'a u32) -> &'a str { 'b: { @@ -127,6 +130,7 @@ up in our face. To make this more clear, we can expand the example: + ```rust,ignore fn as_str<'a>(data: &'a u32) -> &'a str { 'b: { @@ -178,6 +182,7 @@ data.push(4); println!("{}", x); ``` + ```rust,ignore 'a: { let mut data: Vec = vec![1, 2, 3]; diff --git a/src/ownership.md b/src/ownership.md index 4790632..4cfac68 100644 --- a/src/ownership.md +++ b/src/ownership.md @@ -17,7 +17,7 @@ issue...). This is a pervasive problem that C and C++ programs need to deal with. Consider this simple mistake that all of us who have used a non-GC'd language have made at one point: -```rust,ignore +```rust,compile_fail fn as_str(data: &u32) -> &str { // compute the string let s = format!("{}", data); @@ -46,7 +46,7 @@ verifying that references don't escape the scope of their referent. That's because ensuring pointers are always valid is much more complicated than this. For instance in this code, -```rust,ignore +```rust,compile_fail let mut data = vec![1, 2, 3]; // get an internal reference let x = &data[0]; diff --git a/src/panic-handler.md b/src/panic-handler.md index c951772..3b48dba 100644 --- a/src/panic-handler.md +++ b/src/panic-handler.md @@ -17,9 +17,10 @@ Below is shown an example where an application has a different panicking behavio whether is compiled using the dev profile (`cargo build`) or using the release profile (`cargo build --release`). -``` rust, ignore -// crate: panic-semihosting -- log panic messages to the host stderr using semihosting +`panic-semihosting` crate -- log panic messages to the host stderr using semihosting: + +```rust,ignore #![no_std] use core::fmt::{Write, self}; @@ -49,8 +50,10 @@ fn panic(info: &PanicInfo) -> ! { } ``` -``` rust, ignore -// crate: panic-halt -- halt the thread on panic; messages are discarded +`panic-halt` crate -- halt the thread on panic; messages are discarded: + + +```rust,ignore #![no_std] @@ -62,8 +65,10 @@ fn panic(_info: &PanicInfo) -> ! { } ``` -``` rust, ignore -// crate: app +`app` crate: + + +```rust,ignore #![no_std] diff --git a/src/repr-rust.md b/src/repr-rust.md index 7baed6e..5275b23 100644 --- a/src/repr-rust.md +++ b/src/repr-rust.md @@ -104,6 +104,7 @@ Rust lays out the fields in the order specified, we expect it to pad the values in the struct to satisfy their alignment requirements. So if Rust didn't reorder fields, we would expect it to produce the following: + ```rust,ignore struct Foo { count: u16, diff --git a/src/subtyping.md b/src/subtyping.md index 149c3c0..8ac7c1a 100644 --- a/src/subtyping.md +++ b/src/subtyping.md @@ -34,6 +34,7 @@ But unlike normal traits, we can use them as concrete and sized types, just like Now, say we have a very simple function that takes an Animal, like this: + ```rust,ignore fn love(pet: Animal) { pet.snuggle(); @@ -43,6 +44,7 @@ fn love(pet: Animal) { By default, static types must match *exactly* for a program to compile. As such, this code won't compile: + ```rust,ignore let mr_snuggles: Cat = ...; love(mr_snuggles); // ERROR: expected Animal, found Cat @@ -78,6 +80,7 @@ of our static type system, making it worse than useless (and leading to Undefine Here's a simple example of this happening when we apply subtyping in a completely naive "find and replace" way. + ```rust,ignore fn evil_feeder(pet: &mut Animal) { let spike: Dog = ...; @@ -199,6 +202,7 @@ and look at some examples. First off, let's revisit the meowing dog example: + ```rust,ignore fn evil_feeder(pet: &mut Animal) { let spike: Dog = ...; @@ -344,6 +348,7 @@ are guaranteed to be the only one with access to it. Consider the following code: + ```rust,ignore let mr_snuggles: Box = ..; let spike: Box = ..; @@ -369,6 +374,7 @@ Only one thing left to explain: function pointers. To see why `fn(T) -> U` should be covariant over `U`, consider the following signature: + ```rust,ignore fn get_animal() -> Animal; ``` @@ -376,6 +382,7 @@ fn get_animal() -> Animal; This function claims to produce an Animal. As such, it is perfectly valid to provide a function with the following signature instead: + ```rust,ignore fn get_animal() -> Cat; ``` @@ -388,12 +395,14 @@ just forget that fact. However, the same logic does not apply to *arguments*. Consider trying to satisfy: + ```rust,ignore fn handle_animal(Animal); ``` -with +with: + ```rust,ignore fn handle_animal(Cat); ``` diff --git a/src/unbounded-lifetimes.md b/src/unbounded-lifetimes.md index 07f1d6c..03febd6 100644 --- a/src/unbounded-lifetimes.md +++ b/src/unbounded-lifetimes.md @@ -17,6 +17,7 @@ boundaries. Given a function, any output lifetimes that don't derive from inputs are unbounded. For instance: + ```rust,ignore fn get_str<'a>() -> &'a str; ``` diff --git a/src/unchecked-uninit.md b/src/unchecked-uninit.md index 3c6913c..7c07521 100644 --- a/src/unchecked-uninit.md +++ b/src/unchecked-uninit.md @@ -82,6 +82,7 @@ It's worth spending a bit more time on the loop in the middle, and in particular the assignment operator and its interaction with `drop`. If we would have written something like: + ```rust,ignore *x[i].as_mut_ptr() = Box::new(i as u32); // WRONG! ``` diff --git a/src/vec/vec-alloc.md b/src/vec/vec-alloc.md index b18f10e..b301fc6 100644 --- a/src/vec/vec-alloc.md +++ b/src/vec/vec-alloc.md @@ -17,6 +17,7 @@ want to use `dangling` because there's no real allocation to talk about but So: + ```rust,ignore use std::mem; @@ -83,6 +84,7 @@ compiler to be able to reason about data dependencies and aliasing. As a simple example, consider the following fragment of code: + ```rust,ignore *x *= 7; *y *= 3; @@ -162,6 +164,7 @@ such we will guard against this case explicitly. Ok with all the nonsense out of the way, let's actually allocate some memory: + ```rust,ignore use std::alloc::{self, Layout}; diff --git a/src/vec/vec-dealloc.md b/src/vec/vec-dealloc.md index 45fe941..d403f92 100644 --- a/src/vec/vec-dealloc.md +++ b/src/vec/vec-dealloc.md @@ -10,6 +10,7 @@ wouldn't bother unless you notice it's not being stripped (in this case it is). We must not call `alloc::dealloc` when `self.cap == 0`, as in this case we haven't actually allocated any memory. + ```rust,ignore impl Drop for Vec { fn drop(&mut self) { diff --git a/src/vec/vec-deref.md b/src/vec/vec-deref.md index 68d64f7..3021cb0 100644 --- a/src/vec/vec-deref.md +++ b/src/vec/vec-deref.md @@ -11,6 +11,7 @@ All we need is `slice::from_raw_parts`. It will correctly handle empty slices for us. Later once we set up zero-sized type support it will also Just Work for those too. + ```rust,ignore use std::ops::Deref; @@ -26,6 +27,7 @@ impl Deref for Vec { And let's do DerefMut too: + ```rust,ignore use std::ops::DerefMut; diff --git a/src/vec/vec-drain.md b/src/vec/vec-drain.md index 1f4cb85..fbc8edd 100644 --- a/src/vec/vec-drain.md +++ b/src/vec/vec-drain.md @@ -4,6 +4,7 @@ Let's move on to Drain. Drain is largely the same as IntoIter, except that instead of consuming the Vec, it borrows the Vec and leaves its allocation untouched. For now we'll only implement the "basic" full-range version. + ```rust,ignore use std::marker::PhantomData; @@ -26,6 +27,7 @@ impl<'a, T> Iterator for Drain<'a, T> { -- wait, this is seeming familiar. Let's do some more compression. Both IntoIter and Drain have the exact same structure, let's just factor it out. + ```rust,ignore struct RawValIter { start: *const T, @@ -57,6 +59,7 @@ impl RawValIter { And IntoIter becomes the following: + ```rust,ignore pub struct IntoIter { _buf: RawVec, // we don't actually care about this. Just need it to live. @@ -103,6 +106,7 @@ We also take a slice to simplify Drain initialization. Alright, now Drain is really easy: + ```rust,ignore use std::marker::PhantomData; diff --git a/src/vec/vec-insert-remove.md b/src/vec/vec-insert-remove.md index 9138b3e..57283f9 100644 --- a/src/vec/vec-insert-remove.md +++ b/src/vec/vec-insert-remove.md @@ -12,6 +12,7 @@ definitely happen here). If we insert at index `i`, we want to shift the `[i .. len]` to `[i+1 .. len+1]` using the old len. + ```rust,ignore pub fn insert(&mut self, index: usize, elem: T) { // Note: `<=` because it's valid to insert after everything @@ -33,6 +34,7 @@ pub fn insert(&mut self, index: usize, elem: T) { Remove behaves in the opposite manner. We need to shift all the elements from `[i+1 .. len + 1]` to `[i .. len]` using the *new* len. + ```rust,ignore pub fn remove(&mut self, index: usize) -> T { // Note: `<` because it's *not* valid to remove after everything diff --git a/src/vec/vec-into-iter.md b/src/vec/vec-into-iter.md index 39057d4..33327e8 100644 --- a/src/vec/vec-into-iter.md +++ b/src/vec/vec-into-iter.md @@ -42,6 +42,7 @@ dropped. So we're going to use the following struct: + ```rust,ignore pub struct IntoIter { buf: NonNull, @@ -54,6 +55,7 @@ pub struct IntoIter { And this is what we end up with for initialization: + ```rust,ignore impl Vec { pub fn into_iter(self) -> IntoIter { @@ -85,6 +87,7 @@ impl Vec { Here's iterating forward: + ```rust,ignore impl Iterator for IntoIter { type Item = T; @@ -110,6 +113,7 @@ impl Iterator for IntoIter { And here's iterating backwards. + ```rust,ignore impl DoubleEndedIterator for IntoIter { fn next_back(&mut self) -> Option { @@ -129,6 +133,7 @@ Because IntoIter takes ownership of its allocation, it needs to implement Drop to free it. However it also wants to implement Drop to drop any elements it contains that weren't yielded. + ```rust,ignore impl Drop for IntoIter { fn drop(&mut self) { diff --git a/src/vec/vec-layout.md b/src/vec/vec-layout.md index dd97ae1..c1c1afc 100644 --- a/src/vec/vec-layout.md +++ b/src/vec/vec-layout.md @@ -6,6 +6,7 @@ elements that have been initialized. Naively, this means we just want this design: + ```rust,ignore pub struct Vec { ptr: *mut T, diff --git a/src/vec/vec-push-pop.md b/src/vec/vec-push-pop.md index 2366e36..2a4e016 100644 --- a/src/vec/vec-push-pop.md +++ b/src/vec/vec-push-pop.md @@ -17,6 +17,7 @@ target address with the bits of the value we provide. No evaluation involved. For `push`, if the old len (before push was called) is 0, then we want to write to the 0th index. So we should offset by the old len. + ```rust,ignore pub fn push(&mut self, elem: T) { if self.len == self.cap { self.grow(); } @@ -41,6 +42,7 @@ of T there. For `pop`, if the old len is 1, we want to read out of the 0th index. So we should offset by the new len. + ```rust,ignore pub fn pop(&mut self) -> Option { if self.len == 0 { diff --git a/src/vec/vec-raw.md b/src/vec/vec-raw.md index 3fb62b7..f4421db 100644 --- a/src/vec/vec-raw.md +++ b/src/vec/vec-raw.md @@ -8,6 +8,7 @@ time to perform some logic compression. We're going to abstract out the `(ptr, cap)` pair and give them the logic for allocating, growing, and freeing: + ```rust,ignore struct RawVec { ptr: NonNull, @@ -76,6 +77,7 @@ impl Drop for RawVec { And change Vec as follows: + ```rust,ignore pub struct Vec { buf: RawVec, @@ -114,6 +116,7 @@ impl Drop for Vec { And finally we can really simplify IntoIter: + ```rust,ignore pub struct IntoIter { _buf: RawVec, // we don't actually care about this. Just need it to live. diff --git a/src/vec/vec-zsts.md b/src/vec/vec-zsts.md index 500b6f3..3469f0c 100644 --- a/src/vec/vec-zsts.md +++ b/src/vec/vec-zsts.md @@ -29,6 +29,7 @@ overflow for zero-sized types. Due to our current architecture, all this means is writing 3 guards, one in each method of `RawVec`. + ```rust,ignore impl RawVec { fn new() -> Self { @@ -107,6 +108,7 @@ initialize `start` and `end` as the same value, and our iterators will yield nothing. The current solution to this is to cast the pointers to integers, increment, and then cast them back: + ```rust,ignore impl RawValIter { unsafe fn new(slice: &[T]) -> Self { @@ -130,6 +132,7 @@ Also, our size_hint computation code will divide by 0 for ZSTs. Since we'll basically be treating the two pointers as if they point to bytes, we'll just map size 0 to divide by 1. + ```rust,ignore impl Iterator for RawValIter { type Item = T; diff --git a/src/working-with-unsafe.md b/src/working-with-unsafe.md index 0649121..b4b47a1 100644 --- a/src/working-with-unsafe.md +++ b/src/working-with-unsafe.md @@ -84,6 +84,7 @@ impl Vec { This code is simple enough to reasonably audit and informally verify. Now consider adding the following method: + ```rust,ignore fn make_room(&mut self) { // grow the capacity