From 7a82d478a3c9301eda4453ec222842f750e13630 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 11 Apr 2014 14:34:21 -0700 Subject: [PATCH] std: Fix iteration over vectors of 0-size values 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 #13467 --- src/libstd/vec.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index da0e0d73fed..a69120de00f 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -598,7 +598,12 @@ impl Vec { /// ``` #[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::() == 0 { + Slice { data: 1 as *T, len: self.len } + } else { + Slice { data: self.ptr as *T, len: self.len } + }; unsafe { transmute(slice) } } @@ -1335,7 +1340,15 @@ impl Vector for Vec { /// ``` #[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::() == 0 { + Slice { data: 1 as *T, len: self.len } + } else { + Slice { data: self.ptr as *T, len: self.len } + }; unsafe { transmute(slice) } } } @@ -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); + } }