Skip to content

Commit 772e80a

Browse files
authored
Rollup merge of #119917 - Zalathar:split-off, r=cuviper
Remove special-case handling of `vec.split_off(0)` #76682 added special handling to `Vec::split_off` for the case where `at == 0`. Instead of copying the vector's contents into a freshly-allocated vector and returning it, the special-case code steals the old vector's allocation, and replaces it with a new (empty) buffer with the same capacity. That eliminates the need to copy the existing elements, but comes at a surprising cost, as seen in #119913. The returned vector's capacity is no longer determined by the size of its contents (as would be expected for a freshly-allocated vector), and instead uses the full capacity of the old vector. In cases where the capacity is large but the size is small, that results in a much larger capacity than would be expected from reading the documentation of `split_off`. This is especially bad when `split_off` is called in a loop (to recycle a buffer), and the returned vectors have a wide variety of lengths. I believe it's better to remove the special-case code, and treat `at == 0` just like any other value: - The current documentation states that `split_off` returns a “newly allocated vector”, which is not actually true in the current implementation when `at == 0`. - If the value of `at` could be non-zero at runtime, then the caller has already agreed to the cost of a full memcpy of the taken elements in the general case. Avoiding that copy would be nice if it were close to free, but the different handling of capacity means that it is not. - If the caller specifically wants to avoid copying in the case where `at == 0`, they can easily implement that behaviour themselves using `mem::replace`. Fixes #119913.
2 parents a5b60c9 + a655558 commit 772e80a

File tree

2 files changed

+18
-14
lines changed

2 files changed

+18
-14
lines changed

library/alloc/src/vec/mod.rs

-8
Original file line numberDiff line numberDiff line change
@@ -2204,14 +2204,6 @@ impl<T, A: Allocator> Vec<T, A> {
22042204
assert_failed(at, self.len());
22052205
}
22062206

2207-
if at == 0 {
2208-
// the new vector can take over the original buffer and avoid the copy
2209-
return mem::replace(
2210-
self,
2211-
Vec::with_capacity_in(self.capacity(), self.allocator().clone()),
2212-
);
2213-
}
2214-
22152207
let other_len = self.len - at;
22162208
let mut other = Vec::with_capacity_in(other_len, self.allocator().clone());
22172209

library/alloc/tests/vec.rs

+18-6
Original file line numberDiff line numberDiff line change
@@ -958,23 +958,35 @@ fn test_append() {
958958
#[test]
959959
fn test_split_off() {
960960
let mut vec = vec![1, 2, 3, 4, 5, 6];
961+
let orig_ptr = vec.as_ptr();
961962
let orig_capacity = vec.capacity();
962-
let vec2 = vec.split_off(4);
963+
964+
let split_off = vec.split_off(4);
963965
assert_eq!(vec, [1, 2, 3, 4]);
964-
assert_eq!(vec2, [5, 6]);
966+
assert_eq!(split_off, [5, 6]);
965967
assert_eq!(vec.capacity(), orig_capacity);
968+
assert_eq!(vec.as_ptr(), orig_ptr);
966969
}
967970

968971
#[test]
969972
fn test_split_off_take_all() {
970-
let mut vec = vec![1, 2, 3, 4, 5, 6];
973+
// Allocate enough capacity that we can tell whether the split-off vector's
974+
// capacity is based on its size, or (incorrectly) on the original capacity.
975+
let mut vec = Vec::with_capacity(1000);
976+
vec.extend([1, 2, 3, 4, 5, 6]);
971977
let orig_ptr = vec.as_ptr();
972978
let orig_capacity = vec.capacity();
973-
let vec2 = vec.split_off(0);
979+
980+
let split_off = vec.split_off(0);
974981
assert_eq!(vec, []);
975-
assert_eq!(vec2, [1, 2, 3, 4, 5, 6]);
982+
assert_eq!(split_off, [1, 2, 3, 4, 5, 6]);
976983
assert_eq!(vec.capacity(), orig_capacity);
977-
assert_eq!(vec2.as_ptr(), orig_ptr);
984+
assert_eq!(vec.as_ptr(), orig_ptr);
985+
986+
// The split-off vector should be newly-allocated, and should not have
987+
// stolen the original vector's allocation.
988+
assert!(split_off.capacity() < orig_capacity);
989+
assert_ne!(split_off.as_ptr(), orig_ptr);
978990
}
979991

980992
#[test]

0 commit comments

Comments
 (0)