Skip to content

Commit

Permalink
std: Fix iteration over vectors of 0-size values
Browse files Browse the repository at this point in the history
Previously, all slices derived from a vector whose values were of size 0 had a
null pointer as the 'data' pointer on the slice. This caused first pointer to be
yielded during iteration to always be the null pointer. Due to the null pointer
optimization, this meant that the first return value was None, instead of
Some(&T).

This commit changes slice construction from a Vec instance to use a base pointer
of 1 if the values have zero size. This means that the iterator will never
return null, and the iteration will proceed appropriately.

Closes rust-lang#13467
  • Loading branch information
alexcrichton committed Apr 11, 2014
1 parent 8b6091e commit 7a82d47
Showing 1 changed file with 46 additions and 2 deletions.
48 changes: 46 additions & 2 deletions src/libstd/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,12 @@ impl<T> Vec<T> {
/// ```
#[inline]
pub fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] {
let slice = Slice { data: self.ptr as *T, len: self.len };
// See the comment in as_slice() for what's going on here.
let slice = if mem::size_of::<T>() == 0 {
Slice { data: 1 as *T, len: self.len }
} else {
Slice { data: self.ptr as *T, len: self.len }
};
unsafe { transmute(slice) }
}

Expand Down Expand Up @@ -1335,7 +1340,15 @@ impl<T> Vector<T> for Vec<T> {
/// ```
#[inline]
fn as_slice<'a>(&'a self) -> &'a [T] {
let slice = Slice { data: self.ptr as *T, len: self.len };
// If we have a 0-sized vector, then the base pointer should not be NULL
// because an iterator over the slice will attempt to yield the base
// pointer as the first element in the vector, but this will end up
// being Some(NULL) which is optimized to None.
let slice = if mem::size_of::<T>() == 0 {
Slice { data: 1 as *T, len: self.len }
} else {
Slice { data: self.ptr as *T, len: self.len }
};
unsafe { transmute(slice) }
}
}
Expand Down Expand Up @@ -1588,4 +1601,35 @@ mod tests {
vec.retain(|x| x%2 == 0);
assert!(vec == Vec::from_slice([2u, 4]));
}

#[test]
fn zero_sized_values() {
let mut v = Vec::new();
assert_eq!(v.len(), 0);
v.push(());
assert_eq!(v.len(), 1);
v.push(());
assert_eq!(v.len(), 2);
assert_eq!(v.pop(), Some(()));
assert_eq!(v.pop(), Some(()));
assert_eq!(v.pop(), None);

assert_eq!(v.iter().len(), 0);
v.push(());
assert_eq!(v.iter().len(), 1);
v.push(());
assert_eq!(v.iter().len(), 2);

for &() in v.iter() {}

assert_eq!(v.mut_iter().len(), 2);
v.push(());
assert_eq!(v.mut_iter().len(), 3);
v.push(());
assert_eq!(v.mut_iter().len(), 4);

for &() in v.mut_iter() {}
unsafe { v.set_len(0); }
assert_eq!(v.mut_iter().len(), 0);
}
}

1 comment on commit 7a82d47

@thestinger
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r+

Please sign in to comment.