Skip to content

Commit f100c0d

Browse files
committed
---
yaml --- r: 236093 b: refs/heads/stable c: c5a1b87 h: refs/heads/master i: 236091: 8dd8fed v: v3
1 parent e613112 commit f100c0d

File tree

2 files changed

+1
-178
lines changed

2 files changed

+1
-178
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ refs/heads/tmp: afae2ff723393b3ab4ccffef6ac7c6d1809e2da0
2929
refs/tags/1.0.0-alpha.2: 4c705f6bc559886632d3871b04f58aab093bfa2f
3030
refs/tags/homu-tmp: f859507de8c410b648d934d8f5ec1c52daac971d
3131
refs/tags/1.0.0-beta: 8cbb92b53468ee2b0c2d3eeb8567005953d40828
32-
refs/heads/stable: 700895fdd7fcd19ffc00639d9fede532ffeb7952
32+
refs/heads/stable: c5a1b87c6f95e023ab465514aac143bdbd88f56b
3333
refs/tags/1.0.0: 55bd4f8ff2b323f317ae89e254ce87162d52a375
3434
refs/tags/1.1.0: bc3c16f09287e5545c1d3f76b7abd54f2eca868b
3535
refs/tags/1.2.0: f557861f822c34f07270347b94b5280de20a597e

branches/stable/src/doc/tarpl/vec-drain.md

Lines changed: 0 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -139,180 +139,3 @@ impl<T> Vec<T> {
139139

140140

141141

142-
143-
# Handling Zero-Sized Types
144-
145-
It's time. We're going to fight the spectre that is zero-sized types. Safe Rust
146-
*never* needs to care about this, but Vec is very intensive on raw pointers and
147-
raw allocations, which are exactly the *only* two things that care about
148-
zero-sized types. We need to be careful of two things:
149-
150-
* The raw allocator API has undefined behaviour if you pass in 0 for an
151-
allocation size.
152-
* raw pointer offsets are no-ops for zero-sized types, which will break our
153-
C-style pointer iterator.
154-
155-
Thankfully we abstracted out pointer-iterators and allocating handling into
156-
RawValIter and RawVec respectively. How mysteriously convenient.
157-
158-
159-
160-
161-
## Allocating Zero-Sized Types
162-
163-
So if the allocator API doesn't support zero-sized allocations, what on earth
164-
do we store as our allocation? Why, `heap::EMPTY` of course! Almost every operation
165-
with a ZST is a no-op since ZSTs have exactly one value, and therefore no state needs
166-
to be considered to store or load them. This actually extends to `ptr::read` and
167-
`ptr::write`: they won't actually look at the pointer at all. As such we *never* need
168-
to change the pointer.
169-
170-
Note however that our previous reliance on running out of memory before overflow is
171-
no longer valid with zero-sized types. We must explicitly guard against capacity
172-
overflow for zero-sized types.
173-
174-
Due to our current architecture, all this means is writing 3 guards, one in each
175-
method of RawVec.
176-
177-
```rust,ignore
178-
impl<T> RawVec<T> {
179-
fn new() -> Self {
180-
unsafe {
181-
// !0 is usize::MAX. This branch should be stripped at compile time.
182-
let cap = if mem::size_of::<T>() == 0 { !0 } else { 0 };
183-
184-
// heap::EMPTY doubles as "unallocated" and "zero-sized allocation"
185-
RawVec { ptr: Unique::new(heap::EMPTY as *mut T), cap: cap }
186-
}
187-
}
188-
189-
fn grow(&mut self) {
190-
unsafe {
191-
let elem_size = mem::size_of::<T>();
192-
193-
// since we set the capacity to usize::MAX when elem_size is
194-
// 0, getting to here necessarily means the Vec is overfull.
195-
assert!(elem_size != 0, "capacity overflow");
196-
197-
let align = mem::align_of::<T>();
198-
199-
let (new_cap, ptr) = if self.cap == 0 {
200-
let ptr = heap::allocate(elem_size, align);
201-
(1, ptr)
202-
} else {
203-
let new_cap = 2 * self.cap;
204-
let ptr = heap::reallocate(*self.ptr as *mut _,
205-
self.cap * elem_size,
206-
new_cap * elem_size,
207-
align);
208-
(new_cap, ptr)
209-
};
210-
211-
// If allocate or reallocate fail, we'll get `null` back
212-
if ptr.is_null() { oom() }
213-
214-
self.ptr = Unique::new(ptr as *mut _);
215-
self.cap = new_cap;
216-
}
217-
}
218-
}
219-
220-
impl<T> Drop for RawVec<T> {
221-
fn drop(&mut self) {
222-
let elem_size = mem::size_of::<T>();
223-
224-
// don't free zero-sized allocations, as they were never allocated.
225-
if self.cap != 0 && elem_size != 0 {
226-
let align = mem::align_of::<T>();
227-
228-
let num_bytes = elem_size * self.cap;
229-
unsafe {
230-
heap::deallocate(*self.ptr as *mut _, num_bytes, align);
231-
}
232-
}
233-
}
234-
}
235-
```
236-
237-
That's it. We support pushing and popping zero-sized types now. Our iterators
238-
(that aren't provided by slice Deref) are still busted, though.
239-
240-
241-
242-
243-
## Iterating Zero-Sized Types
244-
245-
Zero-sized offsets are no-ops. This means that our current design will always
246-
initialize `start` and `end` as the same value, and our iterators will yield
247-
nothing. The current solution to this is to cast the pointers to integers,
248-
increment, and then cast them back:
249-
250-
```rust,ignore
251-
impl<T> RawValIter<T> {
252-
unsafe fn new(slice: &[T]) -> Self {
253-
RawValIter {
254-
start: slice.as_ptr(),
255-
end: if mem::size_of::<T>() == 0 {
256-
((slice.as_ptr() as usize) + slice.len()) as *const _
257-
} else if slice.len() == 0 {
258-
slice.as_ptr()
259-
} else {
260-
slice.as_ptr().offset(slice.len() as isize)
261-
}
262-
}
263-
}
264-
}
265-
```
266-
267-
Now we have a different bug. Instead of our iterators not running at all, our
268-
iterators now run *forever*. We need to do the same trick in our iterator impls.
269-
Also, our size_hint computation code will divide by 0 for ZSTs. Since we'll
270-
basically be treating the two pointers as if they point to bytes, we'll just
271-
map size 0 to divide by 1.
272-
273-
```rust,ignore
274-
impl<T> Iterator for RawValIter<T> {
275-
type Item = T;
276-
fn next(&mut self) -> Option<T> {
277-
if self.start == self.end {
278-
None
279-
} else {
280-
unsafe {
281-
let result = ptr::read(self.start);
282-
self.start = if mem::size_of::<T>() == 0 {
283-
(self.start as usize + 1) as *const _
284-
} else {
285-
self.start.offset(1);
286-
}
287-
Some(result)
288-
}
289-
}
290-
}
291-
292-
fn size_hint(&self) -> (usize, Option<usize>) {
293-
let elem_size = mem::size_of::<T>();
294-
let len = (self.end as usize - self.start as usize)
295-
/ if elem_size == 0 { 1 } else { elem_size };
296-
(len, Some(len))
297-
}
298-
}
299-
300-
impl<T> DoubleEndedIterator for RawValIter<T> {
301-
fn next_back(&mut self) -> Option<T> {
302-
if self.start == self.end {
303-
None
304-
} else {
305-
unsafe {
306-
self.end = if mem::size_of::<T>() == 0 {
307-
(self.end as usize - 1) as *const _
308-
} else {
309-
self.end.offset(-1);
310-
}
311-
Some(ptr::read(self.end))
312-
}
313-
}
314-
}
315-
}
316-
```
317-
318-
And that's it. Iteration works!

0 commit comments

Comments
 (0)