fix all the doc tests

pull/10/head
Alexis Beingessner 10 years ago committed by Manish Goregaokar
parent 4b9d71becf
commit 9f20f453b2

@ -4,7 +4,7 @@ Like C, all stack variables in Rust are uninitialized until a value is
explicitly assigned to them. Unlike C, Rust statically prevents you from ever explicitly assigned to them. Unlike C, Rust statically prevents you from ever
reading them until you do: reading them until you do:
```rust ```rust,ignore
fn main() { fn main() {
let x: i32; let x: i32;
println!("{}", x); println!("{}", x);
@ -39,7 +39,7 @@ fn main() {
but this doesn't: but this doesn't:
```rust ```rust,ignore
fn main() { fn main() {
let x: i32; let x: i32;
if true { if true {

@ -51,7 +51,7 @@ receivers, see below). If there is an impl for some type `U` and `T` coerces to
following will not type check, even though it is OK to coerce `t` to `&T` and following will not type check, even though it is OK to coerce `t` to `&T` and
there is an impl for `&T`: there is an impl for `&T`:
```rust ```rust,ignore
trait Trait {} trait Trait {}
fn foo<X: Trait>(t: X) {} fn foo<X: Trait>(t: X) {}

@ -3,7 +3,7 @@
What the language *does* provide is full-blown automatic destructors through the What the language *does* provide is full-blown automatic destructors through the
`Drop` trait, which provides the following method: `Drop` trait, which provides the following method:
```rust ```rust,ignore
fn drop(&mut self); fn drop(&mut self);
``` ```
@ -23,13 +23,22 @@ this is totally fine.
For instance, a custom implementation of `Box` might write `Drop` like this: For instance, a custom implementation of `Box` might write `Drop` like this:
```rust ```rust
struct Box<T>{ ptr: *mut T } #![feature(heap_api, core_intrinsics, unique)]
use std::rt::heap;
use std::ptr::Unique;
use std::intrinsics::drop_in_place;
use std::mem;
struct Box<T>{ ptr: Unique<T> }
impl<T> Drop for Box<T> { impl<T> Drop for Box<T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
(*self.ptr).drop(); drop_in_place(*self.ptr);
heap::deallocate(self.ptr); heap::deallocate((*self.ptr) as *mut u8,
mem::size_of::<T>(),
mem::align_of::<T>());
} }
} }
} }
@ -42,25 +51,36 @@ after-free the `ptr` because the Box is immediately marked as uninitialized.
However this wouldn't work: However this wouldn't work:
```rust ```rust
struct Box<T>{ ptr: *mut T } #![feature(heap_api, core_intrinsics, unique)]
use std::rt::heap;
use std::ptr::Unique;
use std::intrinsics::drop_in_place;
use std::mem;
struct Box<T>{ ptr: Unique<T> }
impl<T> Drop for Box<T> { impl<T> Drop for Box<T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
(*self.ptr).drop(); drop_in_place(*self.ptr);
heap::deallocate(self.ptr); heap::deallocate((*self.ptr) as *mut u8,
mem::size_of::<T>(),
mem::align_of::<T>());
} }
} }
} }
struct SuperBox<T> { box: Box<T> } struct SuperBox<T> { my_box: Box<T> }
impl<T> Drop for SuperBox<T> { impl<T> Drop for SuperBox<T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
// Hyper-optimized: deallocate the box's contents for it // Hyper-optimized: deallocate the box's contents for it
// without `drop`ing the contents // without `drop`ing the contents
heap::deallocate(self.box.ptr); heap::deallocate((*self.my_box.ptr) as *mut u8,
mem::size_of::<T>(),
mem::align_of::<T>());
} }
} }
} }
@ -106,18 +126,27 @@ The classic safe solution to overriding recursive drop and allowing moving out
of Self during `drop` is to use an Option: of Self during `drop` is to use an Option:
```rust ```rust
struct Box<T>{ ptr: *mut T } #![feature(heap_api, core_intrinsics, unique)]
use std::rt::heap;
use std::ptr::Unique;
use std::intrinsics::drop_in_place;
use std::mem;
struct Box<T>{ ptr: Unique<T> }
impl<T> Drop for Box<T> { impl<T> Drop for Box<T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
(*self.ptr).drop(); drop_in_place(*self.ptr);
heap::deallocate(self.ptr); heap::deallocate((*self.ptr) as *mut u8,
mem::size_of::<T>(),
mem::align_of::<T>());
} }
} }
} }
struct SuperBox<T> { box: Option<Box<T>> } struct SuperBox<T> { my_box: Option<Box<T>> }
impl<T> Drop for SuperBox<T> { impl<T> Drop for SuperBox<T> {
fn drop(&mut self) { fn drop(&mut self) {
@ -125,7 +154,11 @@ impl<T> Drop for SuperBox<T> {
// Hyper-optimized: deallocate the box's contents for it // Hyper-optimized: deallocate the box's contents for it
// without `drop`ing the contents. Need to set the `box` // without `drop`ing the contents. Need to set the `box`
// field as `None` to prevent Rust from trying to Drop it. // field as `None` to prevent Rust from trying to Drop it.
heap::deallocate(self.box.take().unwrap().ptr); let my_box = self.my_box.take().unwrap();
heap::deallocate((*my_box.ptr) as *mut u8,
mem::size_of::<T>(),
mem::align_of::<T>());
mem::forget(my_box);
} }
} }
} }

@ -31,6 +31,7 @@ And even branched code where all branches have the same behaviour with respect
to initialization: to initialization:
```rust ```rust
# let condition = true;
let mut x = Box::new(0); // x was uninit; just overwrite. let mut x = Box::new(0); // x was uninit; just overwrite.
if condition { if condition {
drop(x) // x gets moved out; make x uninit. drop(x) // x gets moved out; make x uninit.
@ -45,6 +46,7 @@ x = Box::new(0); // x was uninit; just overwrite.
However code like this *requires* runtime information to correctly Drop: However code like this *requires* runtime information to correctly Drop:
```rust ```rust
# let condition = true;
let x; let x;
if condition { if condition {
x = Box::new(0); // x was uninit; just overwrite. x = Box::new(0); // x was uninit; just overwrite.
@ -56,6 +58,7 @@ if condition {
Of course, in this case it's trivial to retrieve static drop semantics: Of course, in this case it's trivial to retrieve static drop semantics:
```rust ```rust
# let condition = true;
if condition { if condition {
let x = Box::new(0); let x = Box::new(0);
println!("{}", x); println!("{}", x);

@ -156,7 +156,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 destructor for the "finally" logic. Whether we panic or not, that destructor
will run and clean up after us. will run and clean up after us.
```rust ```rust,ignore
struct Hole<'a, T: 'a> { struct Hole<'a, T: 'a> {
data: &'a mut [T], data: &'a mut [T],
/// `elt` is always `Some` from new until drop. /// `elt` is always `Some` from new until drop.

@ -28,7 +28,7 @@ fn main() {
If we try to naively desugar this code in the same way that we did in the If we try to naively desugar this code in the same way that we did in the
lifetimes section, we run into some trouble: lifetimes section, we run into some trouble:
```rust ```rust,ignore
struct Closure<F> { struct Closure<F> {
data: (u8, u16), data: (u8, u16),
func: F, func: F,
@ -60,7 +60,7 @@ we enter the body of `call`! Also, that isn't some fixed lifetime; call works wi
This job requires The Magic of Higher-Rank Trait Bounds. The way we desugar This job requires The Magic of Higher-Rank Trait Bounds. The way we desugar
this is as follows: this is as follows:
```rust ```rust,ignore
where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8, where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8,
``` ```

@ -68,7 +68,7 @@ unwinding-safe! Easy!
Now consider the following: Now consider the following:
``` ```rust,ignore
let mut vec = vec![Box::new(0); 4]; let mut vec = vec![Box::new(0); 4];
{ {
@ -118,7 +118,7 @@ Nope.
Let's consider a simplified implementation of Rc: Let's consider a simplified implementation of Rc:
```rust ```rust,ignore
struct Rc<T> { struct Rc<T> {
ptr: *mut RcBox<T>, ptr: *mut RcBox<T>,
} }
@ -183,7 +183,7 @@ in memory.
The thread::scoped API intends to allow threads to be spawned that reference The thread::scoped API intends to allow threads to be spawned that reference
data on the stack without any synchronization over that data. Usage looked like: data on the stack without any synchronization over that data. Usage looked like:
```rust ```rust,ignore
let mut data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let mut data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
{ {
let guards = vec![]; let guards = vec![];
@ -211,7 +211,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! In principle, this totally works! Rust's ownership system perfectly ensures it!
...except it relies on a destructor being called to be safe. ...except it relies on a destructor being called to be safe.
``` ```rust,ignore
let mut data = Box::new(0); let mut data = Box::new(0);
{ {
let guard = thread::scoped(|| { let guard = thread::scoped(|| {

@ -5,7 +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: A *lifetime position* is anywhere you can write a lifetime in a type:
```rust ```rust,ignore
&'a T &'a T
&'a mut T &'a mut T
T<'a> T<'a>
@ -38,7 +38,7 @@ Elision rules are as follows:
Examples: Examples:
```rust ```rust,ignore
fn print(s: &str); // elided fn print(s: &str); // elided
fn print<'a>(s: &'a str); // expanded fn print<'a>(s: &'a str); // expanded

@ -10,8 +10,8 @@ types or lifetimes are logically associated with a struct, but not actually
part of a field. This most commonly occurs with lifetimes. For instance, the `Iter` part of a field. This most commonly occurs with lifetimes. For instance, the `Iter`
for `&'a [T]` is (approximately) defined as follows: for `&'a [T]` is (approximately) defined as follows:
```rust ```rust,ignore
pub struct Iter<'a, T: 'a> { struct Iter<'a, T: 'a> {
ptr: *const T, ptr: *const T,
end: *const T, end: *const T,
} }
@ -33,7 +33,9 @@ Iter logically contains `&'a T`, so this is exactly what we tell
the PhantomData to simulate: the PhantomData to simulate:
``` ```
pub struct Iter<'a, T: 'a> { use std::marker;
struct Iter<'a, T: 'a> {
ptr: *const T, ptr: *const T,
end: *const T, end: *const T,
_marker: marker::PhantomData<&'a T>, _marker: marker::PhantomData<&'a T>,
@ -68,6 +70,8 @@ tell dropck that we *do* own values of type T, and may call destructors of that
type, we must add extra PhantomData: type, we must add extra PhantomData:
``` ```
use std::marker;
struct Vec<T> { struct Vec<T> {
data: *const T, // *const for covariance! data: *const T, // *const for covariance!
len: usize, len: usize,
@ -115,7 +119,7 @@ println!("{} {} {} {}", a, b, c, c2);
However borrowck doesn't understand arrays or slices in any way, so this doesn't However borrowck doesn't understand arrays or slices in any way, so this doesn't
work: work:
```rust ```rust,ignore
let x = [1, 2, 3]; let x = [1, 2, 3];
let a = &mut x[0]; let a = &mut x[0];
let b = &mut x[1]; let b = &mut x[1];
@ -144,7 +148,7 @@ left of the index, and one for everything to the right. Intuitively we know this
is safe because the slices don't alias. However the implementation requires some is safe because the slices don't alias. However the implementation requires some
unsafety: unsafety:
```rust ```rust,ignore
fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
unsafe { unsafe {
let self2: &mut [T] = mem::transmute_copy(&self); let self2: &mut [T] = mem::transmute_copy(&self);
@ -189,8 +193,8 @@ Whether it's raw pointers, or safely composing on top of *another* IterMut.
For instance, VecDeque's IterMut: For instance, VecDeque's IterMut:
```rust ```rust,ignore
pub struct IterMut<'a, T:'a> { struct IterMut<'a, T:'a> {
// The whole backing array. Some of these indices are initialized! // The whole backing array. Some of these indices are initialized!
ring: &'a mut [T], ring: &'a mut [T],
tail: usize, tail: usize,

@ -38,7 +38,7 @@ let z = &y;
The borrow checker always tries to minimize the extent of a lifetime, so it will The borrow checker always tries to minimize the extent of a lifetime, so it will
likely desugar to the following: likely desugar to the following:
```rust ```rust,ignore
// NOTE: `'a: {` and `&'b x` is not valid syntax! // NOTE: `'a: {` and `&'b x` is not valid syntax!
'a: { 'a: {
let x: i32 = 0; let x: i32 = 0;
@ -69,8 +69,8 @@ z = y;
The borrow checker always tries to minimize the extent of a lifetime, so it will The borrow checker always tries to minimize the extent of a lifetime, so it will
likely desugar to something like the following: likely desugar to something like the following:
```rust ```rust,ignore
// NOTE: `'a: {` and `&'b x` is not valid syntax! // NOTE: `'a: {` and `foo = &'b x` is not valid syntax!
'a: { 'a: {
let x: i32 = 0; let x: i32 = 0;
'b: { 'b: {
@ -174,14 +174,14 @@ our implementation *just a bit*.)
How about the other example: How about the other example:
```rust ```rust,ignore
let mut data = vec![1, 2, 3]; let mut data = vec![1, 2, 3];
let x = &data[0]; let x = &data[0];
data.push(4); data.push(4);
println!("{}", x); println!("{}", x);
``` ```
```rust ```rust,ignore
'a: { 'a: {
let mut data: Vec<i32> = vec![1, 2, 3]; let mut data: Vec<i32> = vec![1, 2, 3];
'b: { 'b: {

@ -16,7 +16,7 @@ issue...). This is a pervasive problem that C and C++ need to deal with.
Consider this simple mistake that all of us who have used a non-GC'd language Consider this simple mistake that all of us who have used a non-GC'd language
have made at one point: have made at one point:
```rust ```rust,ignore
fn as_str(data: &u32) -> &str { fn as_str(data: &u32) -> &str {
// compute the string // compute the string
let s = format!("{}", data); let s = format!("{}", data);
@ -45,7 +45,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. because ensuring pointers are always valid is much more complicated than this.
For instance in this code, For instance in this code,
```rust ```rust,ignore
let mut data = vec![1, 2, 3]; let mut data = vec![1, 2, 3];
// get an internal reference // get an internal reference
let x = &data[0]; let x = &data[0];

@ -67,7 +67,7 @@ fields in the order specified, we expect it to *pad* the values in the struct to
their *alignment* requirements. So if Rust didn't reorder fields, we would expect Rust to their *alignment* requirements. So if Rust didn't reorder fields, we would expect Rust to
produce the following: produce the following:
```rust ```rust,ignore
struct Foo<u16, u32> { struct Foo<u16, u32> {
count: u16, count: u16,
data1: u16, data1: u16,

@ -56,6 +56,8 @@ In the *incredibly rare* case that a type is *inappropriately* automatically
derived to be Send or Sync, then one can also *unimplement* Send and Sync: derived to be Send or Sync, then one can also *unimplement* Send and Sync:
```rust ```rust
#![feature(optin_builtin_traits)]
struct SpecialThreadToken(u8); struct SpecialThreadToken(u8);
impl !Send for SpecialThreadToken {} impl !Send for SpecialThreadToken {}

@ -104,7 +104,7 @@ However what should happen when passing *by-value* is less obvious. It turns out
that, yes, you can use subtyping when passing by-value. That is, this works: that, yes, you can use subtyping when passing by-value. That is, this works:
```rust ```rust
fn get_box<'a>(&'a u8) -> Box<&'a str> { fn get_box<'a>(str: &'a u8) -> Box<&'a str> {
// string literals are `&'static str`s // string literals are `&'static str`s
Box::new("hello") Box::new("hello")
} }
@ -123,7 +123,7 @@ must be invariant to avoid lifetime smuggling.
`Fn(T) -> U` should be invariant over T, consider the following function `Fn(T) -> U` should be invariant over T, consider the following function
signature: signature:
```rust ```rust,ignore
// 'a is derived from some parent scope // 'a is derived from some parent scope
fn foo(&'a str) -> usize; fn foo(&'a str) -> usize;
``` ```
@ -131,7 +131,7 @@ fn foo(&'a str) -> usize;
This signature claims that it can handle any `&str` that lives *at least* as long This signature claims that it can handle any `&str` that lives *at least* as long
as `'a`. Now if this signature was variant with respect to `&str`, that would mean as `'a`. Now if this signature was variant with respect to `&str`, that would mean
```rust ```rust,ignore
fn foo(&'static str) -> usize; fn foo(&'static str) -> usize;
``` ```
@ -142,7 +142,7 @@ and nothing else. Therefore functions are not variant over their arguments.
To see why `Fn(T) -> U` should be *variant* over U, consider the following To see why `Fn(T) -> U` should be *variant* over U, consider the following
function signature: function signature:
```rust ```rust,ignore
// 'a is derived from some parent scope // 'a is derived from some parent scope
fn foo(usize) -> &'a str; fn foo(usize) -> &'a str;
``` ```
@ -150,7 +150,7 @@ fn foo(usize) -> &'a str;
This signature claims that it will return something that outlives `'a`. It is This signature claims that it will return something that outlives `'a`. It is
therefore completely reasonable to provide therefore completely reasonable to provide
```rust ```rust,ignore
fn foo(usize) -> &'static str; fn foo(usize) -> &'static str;
``` ```
@ -171,15 +171,17 @@ in multiple fields.
* Otherwise, Foo is invariant over A * Otherwise, Foo is invariant over A
```rust ```rust
struct Foo<'a, 'b, A, B, C, D, E, F, G, H> { use std::cell::Cell;
struct Foo<'a, 'b, A: 'a, B: 'b, C, D, E, F, G, H> {
a: &'a A, // variant over 'a and A a: &'a A, // variant over 'a and A
b: &'b mut B, // invariant over 'b and B b: &'b mut B, // invariant over 'b and B
c: *const C, // variant over C c: *const C, // variant over C
d: *mut D, // invariant over D d: *mut D, // invariant over D
e: Vec<E>, // variant over E e: Vec<E>, // variant over E
f: Cell<F>, // invariant over F f: Cell<F>, // invariant over F
g: G // variant over G g: G, // variant over G
h1: H // would also be variant over H except... h1: H, // would also be variant over H except...
h2: Cell<H> // invariant over H, because invariance wins h2: Cell<H>, // invariant over H, because invariance wins
} }
``` ```

@ -17,7 +17,7 @@ boundaries.
Given a function, any output lifetimes that don't derive from inputs are Given a function, any output lifetimes that don't derive from inputs are
unbounded. For instance: unbounded. For instance:
```rust ```rust,ignore
fn get_str<'a>() -> &'a str; fn get_str<'a>() -> &'a str;
``` ```

@ -46,27 +46,26 @@ locations of memory can break things are basically uncountable!
Putting this all together, we get the following: Putting this all together, we get the following:
```rust ```rust
fn main() { use std::mem;
use std::mem; use std::ptr;
// size of the array is hard-coded but easy to change. This means we can't // size of the array is hard-coded but easy to change. This means we can't
// use [a, b, c] syntax to initialize the array, though! // use [a, b, c] syntax to initialize the array, though!
const SIZE = 10; const SIZE: usize = 10;
let x: [Box<u32>; SIZE]; let mut x: [Box<u32>; SIZE];
unsafe { unsafe {
// convince Rust that x is Totally Initialized // convince Rust that x is Totally Initialized
x = mem::uninitialized(); x = mem::uninitialized();
for i in 0..SIZE { for i in 0..SIZE {
// very carefully overwrite each index without reading it // very carefully overwrite each index without reading it
// NOTE: exception safety is not a concern; Box can't panic // NOTE: exception safety is not a concern; Box can't panic
ptr::write(&mut x[i], Box::new(i)); ptr::write(&mut x[i], Box::new(i as u32));
}
} }
println!("{}", x);
} }
println!("{:?}", x);
``` ```
It's worth noting that you don't need to worry about ptr::write-style It's worth noting that you don't need to worry about ptr::write-style

@ -2,7 +2,7 @@
So: So:
```rust ```rust,ignore
#![feature(heap_api)] #![feature(heap_api)]
use std::rt::heap::EMPTY; use std::rt::heap::EMPTY;
@ -69,7 +69,7 @@ Anything else will use up too much space.
However since this is a tutorial, we're not going to be particularly optimal here, However since this is a tutorial, we're not going to be particularly optimal here,
and just unconditionally check, rather than use clever platform-specific `cfg`s. and just unconditionally check, rather than use clever platform-specific `cfg`s.
```rust ```rust,ignore
fn grow(&mut self) { fn grow(&mut self) {
// this is all pretty delicate, so let's say it's all unsafe // this is all pretty delicate, so let's say it's all unsafe
unsafe { unsafe {

@ -11,13 +11,13 @@ We must not call `heap::deallocate` when `self.cap == 0`, as in this case we hav
actually allocated any memory. actually allocated any memory.
```rust ```rust,ignore
impl<T> Drop for Vec<T> { impl<T> Drop for Vec<T> {
fn drop(&mut self) { fn drop(&mut self) {
if self.cap != 0 { if self.cap != 0 {
while let Some(_) = self.pop() { } while let Some(_) = self.pop() { }
let align = mem::min_align_of::<T>(); let align = mem::align_of::<T>();
let elem_size = mem::size_of::<T>(); let elem_size = mem::size_of::<T>();
let num_bytes = elem_size * self.cap; let num_bytes = elem_size * self.cap;
unsafe { unsafe {

@ -9,7 +9,7 @@ conditions.
All we need is `slice::from_raw_parts`. All we need is `slice::from_raw_parts`.
```rust ```rust,ignore
use std::ops::Deref; use std::ops::Deref;
impl<T> Deref for Vec<T> { impl<T> Deref for Vec<T> {
@ -24,7 +24,7 @@ impl<T> Deref for Vec<T> {
And let's do DerefMut too: And let's do DerefMut too:
```rust ```rust,ignore
use std::ops::DerefMut; use std::ops::DerefMut;
impl<T> DerefMut for Vec<T> { impl<T> DerefMut for Vec<T> {

@ -51,7 +51,7 @@ impl<T> RawValIter<T> {
And IntoIter becomes the following: And IntoIter becomes the following:
``` ```rust,ignore
pub struct IntoIter<T> { pub struct IntoIter<T> {
_buf: RawVec<T>, // we don't actually care about this. Just need it to live. _buf: RawVec<T>, // we don't actually care about this. Just need it to live.
iter: RawValIter<T>, iter: RawValIter<T>,
@ -96,7 +96,7 @@ We also take a slice to simplify Drain initialization.
Alright, now Drain is really easy: Alright, now Drain is really easy:
```rust ```rust,ignore
use std::marker::PhantomData; use std::marker::PhantomData;
pub struct Drain<'a, T: 'a> { pub struct Drain<'a, T: 'a> {
@ -174,7 +174,7 @@ overflow for zero-sized types.
Due to our current architecture, all this means is writing 3 guards, one in each Due to our current architecture, all this means is writing 3 guards, one in each
method of RawVec. method of RawVec.
```rust ```rust,ignore
impl<T> RawVec<T> { impl<T> RawVec<T> {
fn new() -> Self { fn new() -> Self {
unsafe { unsafe {
@ -194,7 +194,7 @@ impl<T> RawVec<T> {
// 0, getting to here necessarily means the Vec is overfull. // 0, getting to here necessarily means the Vec is overfull.
assert!(elem_size != 0, "capacity overflow"); assert!(elem_size != 0, "capacity overflow");
let align = mem::min_align_of::<T>(); let align = mem::align_of::<T>();
let (new_cap, ptr) = if self.cap == 0 { let (new_cap, ptr) = if self.cap == 0 {
let ptr = heap::allocate(elem_size, align); let ptr = heap::allocate(elem_size, align);
@ -223,7 +223,7 @@ impl<T> Drop for RawVec<T> {
// don't free zero-sized allocations, as they were never allocated. // don't free zero-sized allocations, as they were never allocated.
if self.cap != 0 && elem_size != 0 { if self.cap != 0 && elem_size != 0 {
let align = mem::min_align_of::<T>(); let align = mem::align_of::<T>();
let num_bytes = elem_size * self.cap; let num_bytes = elem_size * self.cap;
unsafe { unsafe {
@ -247,7 +247,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, nothing. The current solution to this is to cast the pointers to integers,
increment, and then cast them back: increment, and then cast them back:
``` ```rust,ignore
impl<T> RawValIter<T> { impl<T> RawValIter<T> {
unsafe fn new(slice: &[T]) -> Self { unsafe fn new(slice: &[T]) -> Self {
RawValIter { RawValIter {
@ -270,7 +270,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 basically be treating the two pointers as if they point to bytes, we'll just
map size 0 to divide by 1. map size 0 to divide by 1.
``` ```rust,ignore
impl<T> Iterator for RawValIter<T> { impl<T> Iterator for RawValIter<T> {
type Item = T; type Item = T;
fn next(&mut self) -> Option<T> { fn next(&mut self) -> Option<T> {

@ -38,7 +38,7 @@ impl<T> RawVec<T> {
// 0, getting to here necessarily means the Vec is overfull. // 0, getting to here necessarily means the Vec is overfull.
assert!(elem_size != 0, "capacity overflow"); assert!(elem_size != 0, "capacity overflow");
let align = mem::min_align_of::<T>(); let align = mem::align_of::<T>();
let (new_cap, ptr) = if self.cap == 0 { let (new_cap, ptr) = if self.cap == 0 {
let ptr = heap::allocate(elem_size, align); let ptr = heap::allocate(elem_size, align);
@ -65,7 +65,7 @@ impl<T> Drop for RawVec<T> {
fn drop(&mut self) { fn drop(&mut self) {
let elem_size = mem::size_of::<T>(); let elem_size = mem::size_of::<T>();
if self.cap != 0 && elem_size != 0 { if self.cap != 0 && elem_size != 0 {
let align = mem::min_align_of::<T>(); let align = mem::align_of::<T>();
let num_bytes = elem_size * self.cap; let num_bytes = elem_size * self.cap;
unsafe { unsafe {
@ -306,4 +306,6 @@ impl<'a, T> Drop for Drain<'a, T> {
fn oom() { fn oom() {
::std::process::exit(-9999); ::std::process::exit(-9999);
} }
# fn main() {}
``` ```

@ -11,7 +11,7 @@ here).
If we insert at index `i`, we want to shift the `[i .. len]` to `[i+1 .. len+1]` If we insert at index `i`, we want to shift the `[i .. len]` to `[i+1 .. len+1]`
using the *old* len. using the *old* len.
```rust ```rust,ignore
pub fn insert(&mut self, index: usize, elem: T) { pub fn insert(&mut self, index: usize, elem: T) {
// Note: `<=` because it's valid to insert after everything // Note: `<=` because it's valid to insert after everything
// which would be equivalent to push. // which would be equivalent to push.
@ -34,7 +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 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. `[i+1 .. len + 1]` to `[i .. len]` using the *new* len.
```rust ```rust,ignore
pub fn remove(&mut self, index: usize) -> T { pub fn remove(&mut self, index: usize) -> T {
// Note: `<` because it's *not* valid to remove after everything // Note: `<` because it's *not* valid to remove after everything
assert!(index < self.len, "index out of bounds"); assert!(index < self.len, "index out of bounds");

@ -37,7 +37,7 @@ indistinguishable from the case where there are no more elements to yield.
So we're going to use the following struct: So we're going to use the following struct:
```rust ```rust,ignore
struct IntoIter<T> { struct IntoIter<T> {
buf: Unique<T>, buf: Unique<T>,
cap: usize, cap: usize,
@ -63,7 +63,7 @@ cap or len being 0 to not do the offset.
So this is what we end up with for initialization: So this is what we end up with for initialization:
```rust ```rust,ignore
impl<T> Vec<T> { impl<T> Vec<T> {
fn into_iter(self) -> IntoIter<T> { fn into_iter(self) -> IntoIter<T> {
// Can't destructure Vec since it's Drop // Can't destructure Vec since it's Drop
@ -93,7 +93,7 @@ impl<T> Vec<T> {
Here's iterating forward: Here's iterating forward:
```rust ```rust,ignore
impl<T> Iterator for IntoIter<T> { impl<T> Iterator for IntoIter<T> {
type Item = T; type Item = T;
fn next(&mut self) -> Option<T> { fn next(&mut self) -> Option<T> {
@ -118,7 +118,7 @@ impl<T> Iterator for IntoIter<T> {
And here's iterating backwards. And here's iterating backwards.
```rust ```rust,ignore
impl<T> DoubleEndedIterator for IntoIter<T> { impl<T> DoubleEndedIterator for IntoIter<T> {
fn next_back(&mut self) -> Option<T> { fn next_back(&mut self) -> Option<T> {
if self.start == self.end { if self.start == self.end {
@ -138,14 +138,14 @@ to free it. However it *also* wants to implement Drop to drop any elements it
contains that weren't yielded. contains that weren't yielded.
```rust ```rust,ignore
impl<T> Drop for IntoIter<T> { impl<T> Drop for IntoIter<T> {
fn drop(&mut self) { fn drop(&mut self) {
if self.cap != 0 { if self.cap != 0 {
// drop any remaining elements // drop any remaining elements
for _ in &mut *self {} for _ in &mut *self {}
let align = mem::min_align_of::<T>(); let align = mem::align_of::<T>();
let elem_size = mem::size_of::<T>(); let elem_size = mem::size_of::<T>();
let num_bytes = elem_size * self.cap; let num_bytes = elem_size * self.cap;
unsafe { unsafe {
@ -164,7 +164,7 @@ compression.
We're going to abstract out the `(ptr, cap)` pair and give them the logic for We're going to abstract out the `(ptr, cap)` pair and give them the logic for
allocating, growing, and freeing: allocating, growing, and freeing:
```rust ```rust,ignore
struct RawVec<T> { struct RawVec<T> {
ptr: Unique<T>, ptr: Unique<T>,
@ -182,7 +182,7 @@ impl<T> RawVec<T> {
// unchanged from Vec // unchanged from Vec
fn grow(&mut self) { fn grow(&mut self) {
unsafe { unsafe {
let align = mem::min_align_of::<T>(); let align = mem::align_of::<T>();
let elem_size = mem::size_of::<T>(); let elem_size = mem::size_of::<T>();
let (new_cap, ptr) = if self.cap == 0 { let (new_cap, ptr) = if self.cap == 0 {
@ -210,7 +210,7 @@ impl<T> RawVec<T> {
impl<T> Drop for RawVec<T> { impl<T> Drop for RawVec<T> {
fn drop(&mut self) { fn drop(&mut self) {
if self.cap != 0 { if self.cap != 0 {
let align = mem::min_align_of::<T>(); let align = mem::align_of::<T>();
let elem_size = mem::size_of::<T>(); let elem_size = mem::size_of::<T>();
let num_bytes = elem_size * self.cap; let num_bytes = elem_size * self.cap;
unsafe { unsafe {
@ -223,7 +223,7 @@ impl<T> Drop for RawVec<T> {
And change vec as follows: And change vec as follows:
```rust ```rust,ignore
pub struct Vec<T> { pub struct Vec<T> {
buf: RawVec<T>, buf: RawVec<T>,
len: usize, len: usize,
@ -254,14 +254,14 @@ impl<T> Drop for Vec<T> {
And finally we can really simplify IntoIter: And finally we can really simplify IntoIter:
```rust ```rust,ignore
struct IntoIter<T> { struct IntoIter<T> {
_buf: RawVec<T>, // we don't actually care about this. Just need it to live. _buf: RawVec<T>, // we don't actually care about this. Just need it to live.
start: *const T, start: *const T,
end: *const T, end: *const T,
} }
// next and next_back litterally unchanged since they never referred to the buf // next and next_back literally unchanged since they never referred to the buf
impl<T> Drop for IntoIter<T> { impl<T> Drop for IntoIter<T> {
fn drop(&mut self) { fn drop(&mut self) {

@ -4,11 +4,13 @@ First off, we need to come up with the struct layout. Naively we want this
design: design:
```rust ```rust
struct Vec<T> { pub struct Vec<T> {
ptr: *mut T, ptr: *mut T,
cap: usize, cap: usize,
len: usize, len: usize,
} }
# fn main() {}
``` ```
And indeed this would compile. Unfortunately, it would be incorrect. The compiler And indeed this would compile. Unfortunately, it would be incorrect. The compiler
@ -32,6 +34,8 @@ pub struct Vec<T> {
cap: usize, cap: usize,
len: usize, len: usize,
} }
# fn main() {}
``` ```
As a recap, Unique is a wrapper around a raw pointer that declares that: As a recap, Unique is a wrapper around a raw pointer that declares that:

@ -17,7 +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 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. to the 0th index. So we should offset by the old len.
```rust ```rust,ignore
pub fn push(&mut self, elem: T) { pub fn push(&mut self, elem: T) {
if self.len == self.cap { self.grow(); } if self.len == self.cap { self.grow(); }
@ -41,7 +41,7 @@ of T there.
For `pop`, if the old len is 1, we want to read out of the 0th index. So we 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. should offset by the *new* len.
```rust ```rust,ignore
pub fn pop(&mut self) -> Option<T> { pub fn pop(&mut self) -> Option<T> {
if self.len == 0 { if self.len == 0 {
None None

@ -5,7 +5,7 @@ binary manner. Unfortunately, reality is significantly more complicated than tha
For instance, consider the following toy function: For instance, consider the following toy function:
```rust ```rust
pub fn index(idx: usize, arr: &[u8]) -> Option<u8> { fn index(idx: usize, arr: &[u8]) -> Option<u8> {
if idx < arr.len() { if idx < arr.len() {
unsafe { unsafe {
Some(*arr.get_unchecked(idx)) Some(*arr.get_unchecked(idx))
@ -22,7 +22,7 @@ function, the scope of the unsafe block is questionable. Consider changing the
`<` to a `<=`: `<` to a `<=`:
```rust ```rust
pub fn index(idx: usize, arr: &[u8]) -> Option<u8> { fn index(idx: usize, arr: &[u8]) -> Option<u8> {
if idx <= arr.len() { if idx <= arr.len() {
unsafe { unsafe {
Some(*arr.get_unchecked(idx)) Some(*arr.get_unchecked(idx))
@ -44,7 +44,9 @@ Trickier than that is when we get into actual statefulness. Consider a simple
implementation of `Vec`: implementation of `Vec`:
```rust ```rust
// Note this definition is insufficient. See the section on lifetimes. use std::ptr;
// Note this definition is insufficient. See the section on implementing Vec.
pub struct Vec<T> { pub struct Vec<T> {
ptr: *mut T, ptr: *mut T,
len: usize, len: usize,
@ -61,21 +63,25 @@ impl<T> Vec<T> {
self.reallocate(); self.reallocate();
} }
unsafe { unsafe {
ptr::write(self.ptr.offset(len as isize), elem); ptr::write(self.ptr.offset(self.len as isize), elem);
self.len += 1; self.len += 1;
} }
} }
# fn reallocate(&mut self) { }
} }
# fn main() {}
``` ```
This code is simple enough to reasonably audit and verify. Now consider This code is simple enough to reasonably audit and verify. Now consider
adding the following method: adding the following method:
```rust ```rust,ignore
fn make_room(&mut self) { fn make_room(&mut self) {
// grow the capacity // grow the capacity
self.cap += 1; self.cap += 1;
} }
``` ```
This code is safe, but it is also completely unsound. Changing the capacity This code is safe, but it is also completely unsound. Changing the capacity

Loading…
Cancel
Save