Do not inline finish_grow
Fixes#78471.
Looking at libgkrust.a in Firefox, the sizes for the `gkrust.*.o` file is:
- 18584816 (text) 582418 (data) with unmodified master
- 17937659 (text) 582554 (data) with #72227 reverted
- 17968228 (text) 582858 (data) with `#[inline(never)]` on `grow_amortized` and `grow_exact`, but that has some performance consequences
- 17927760 (text) 582322 (data) with this change
So in terms of size, at least in the case of Firefox, this patch more than undoes the regression. I don't think it should affect performance, but we'll see.
We also change the specialization of `SpecFromIterNested::from_iter` for
`TrustedLen` to use `Vec::with_capacity` when the iterator has a proper size
hint, instead of `Vec::new`, avoiding calls to `grow_*` and thus
`finish_grow` in some fully inlinable cases, which would regress with
this change.
Fixes#78471.
Change slice::to_vec to not use extend_from_slice
I saw this [Zulip thread](216164455), and didn't see any update from it, so I thought I'd try to fix it. This converts `to_vec` to no longer use `extend_from_slice`, but relies on knowing that the allocated capacity is the same size as the input.
[Godbolt new v1](https://rust.godbolt.org/z/1bcWKG)
[Godbolt new v2 w/ drop guard](https://rust.godbolt.org/z/5jn76K)
[Godbolt old version](https://rust.godbolt.org/z/e4ePav)
After some amount of iteration, there are now two specializations for `to_vec`, one for `Copy` types that use memcpy, and one for clone types which is the original from this PR.
This is then used inside of `impl<T: Clone> FromIterator<Iter::Slice<T>> for Vec<T>` which is essentially equivalent to `&[T] -> Vec<T>`, instead of previous specialization of the `extend` function. This is because extend has to reason more about existing capacity by calling `reserve` on an existing vec, and thus produces worse asm.
Downsides: This allocates the exact capacity, so I think if many items are added to this `Vec` after, it might need to allocate whereas extending may not. I also noticed the number of faults went up in the benchmarks, but not sure where from exactly.
This also required adding a loop guard in case clone panics
Add specialization for copy
There is a better version for copy, so I've added specialization for that function
and hopefully that should speed it up even more.
Switch FromIter<slice::Iter> to use `to_vec`
Test different unrolling version for to_vec
Revert to impl
From benchmarking, it appears this version is faster
More consistently use spaces after commas in lists in docs
This PR changes instances of lists that didn't use spaces after commas, like `vec![1,2,3]`, to `vec![1, 2, 3]` to be more consistent with idiomatic Rust style (the way these were looks strange to me, especially because there are often lists that *do* use spaces after the commas later in the same code block 😬).
I noticed one of these in an example in the stdlib docs and went looking for more, but as far as I can see, I'm only changing those spots in user-facing documentation or rustc output, and the changes make no semantic difference.
Doc formating consistency between slice sort and sort_unstable, and big O notation consistency
Updated documentation for slice sorting methods to be consistent between stable and unstable versions, which just ended up being minor formatting differences.
I also went through and updated any doc comments with big O notation to be consistent with #74010 by italicizing them rather than having them in a code block.
Move `slice::check_range` to `RangeBounds`
Since this method doesn't take a slice anymore (#76662), it makes more sense to define it on `RangeBounds`.
Questions:
- Should the new method be `assert_len` or `assert_length`?
Add PartialEq impls for Vec <-> slice
This is a follow-up to #71660 and rust-lang/rfcs#2917 to add two more missing vec/slice PartialEq impls:
```
impl<A, B> PartialEq<[B]> for Vec<A> where A: PartialEq<B> { .. }
impl<A, B> PartialEq<Vec<B>> for [A] where A: PartialEq<B> { .. }
```
Since this is insta-stable, it should go through the `@rust-lang/libs` FCP process. Note that I used version 1.47.0 for the `stable` attribute because I assume this will not merge before the 1.46.0 branch is cut next week.
It's possible for method resolution to pick this method over a lower
priority stable method, causing compilation errors. Since this method
is permanently unstable, give it a name that is very unlikely to be used
in user code.
Add `[T; N]: TryFrom<Vec<T>>` (insta-stable)
This is very similar to the [existing](https://doc.rust-lang.org/nightly/std/convert/trait.TryFrom.html#impl-TryFrom%3CBox%3C%5BT%5D%3E%3E) `Box<[T; N]>: TryFrom<Box<[T]>>`, but allows avoiding the `shrink_to_fit` if you have a vector and not a boxed slice.
Like the slice equivalents of this, it fails if the length of the vector is not exactly `N`.
This uses `Vec<T>` as the `Error` type to return the input, like how the `Rc<[T]> -> Rc<[T; N]>` (and Arc) ones also reflect the input directly in the error type.
```rust
#[stable(feature = "array_try_from_vec", since = "1.47.0")]
impl<T, const N: usize> TryFrom<Vec<T>> for [T; N] {
type Error = Vec<T>;
fn try_from(mut vec: Vec<T>) -> Result<[T; N], Vec<T>>;
}
```
Inspired by this zulip thread: 209048103
Fix liballoc test suite for Miri
Mostly, fix the regression introduced by https://github.com/rust-lang/rust/pull/75207 that caused slices (i.e., references) to be created to invalid memory or memory that has aliasing pointers that we want to keep valid. @dylni this changes the type of `check_range` to only require the length, not the full reference to the slice, which indeed is all the information this function requires.
Also reduce the size of a test introduced in https://github.com/rust-lang/rust/pull/70793 to make it not take 3 minutes in Miri.
This makes https://github.com/RalfJung/miri-test-libstd work again.
Optimize behavior of vec.split_off(0) (take all)
Optimization improvement to `split_off()` so the performance meets the
intuitively expected behavior when `at == 0`, avoiding the current behavior
of copying the entire vector.
The change honors documented behavior that the original vector's
"previous capacity unchanged".
This improvement better supports the pattern for building and flushing a
buffer of elements, such as the following:
```rust
let mut vec = Vec::new();
loop {
vec.push(something);
if condition_is_met {
process(vec.split_off(0));
}
}
```
`Option` wrapping is the first alternative I thought of, but is much
less obvious and more verbose:
```rust
let mut capacity = 1;
let mut vec: Option<Vec<Stuff>> = None;
loop {
vec.get_or_insert_with(|| Vec::with_capacity(capacity)).push(something);
if condition_is_met {
capacity = vec.capacity();
process(vec.take().unwrap());
}
}
```
Directly using `mem::replace()` (instead of calling`split_off()`) could work,
but `mem::replace()` is a more advanced tool for Rust developers, and in
this case, I believe developers would assume the standard library should
be sufficient for the purpose described here.
The benefit of the approach to this change is it does not change the
existing API contract, but improves the peformance of `split_off(0)` for
`Vec`, `String` (which delegates `split_off()` to `Vec`), and any other
existing use cases.
This change adds tests to validate the behavior of `split_off()` with
regard to capacity, as originally documented, and confirm that behavior
still holds, when `at == 0`.
The change is an implementation detail, and does not require a
documentation change, but documenting the new behavior as part of its
API contract may benefit future users.
(Let me know if I should make that documentation update.)
Note, for future consideration:
I think it would be helpful to introduce an additional method to `Vec`
(if not also to `String`):
```
pub fn take_all(&mut self) -> Self {
self.split_off(0)
}
```
This would make it more clear how `Vec` supports the pattern, and make
it easier to find, since the behavior is similar to other `take()`
methods in the Rust standard library.
r? `@wesleywiser`
FYI: `@tmandry`
Optimization improvement to `split_off()` so the performance meets the
intuitively expected behavior when `at == 0`, avoiding the current
behavior of copying the entire vector.
The change honors documented behavior that the method leaves the
original vector's "previous capacity unchanged".
This improvement better supports the pattern for building and flushing a
buffer of elements, such as the following:
```rust
let mut vec = Vec::new();
loop {
vec.push(something);
if condition_is_met {
process(vec.split_off(0));
}
}
```
`Option` wrapping is the first alternative I thought of, but is much
less obvious and more verbose:
```rust
let mut capacity = 1;
let mut vec: Option<Vec<Stuff>> = None;
loop {
vec.get_or_insert_with(|| Vec::with_capacity(capacity)).push(something);
if condition_is_met {
capacity = vec.capacity();
process(vec.take().unwrap());
}
}
```
Directly applying `mem::replace()` could work, but `mem::` functions are
typically a last resort, when a developer is actively seeking better
performance than the standard library provides, for example.
The benefit of the approach to this change is it does not change the
existing API contract, but improves the peformance of `split_off(0)` for
`Vec`, `String` (which delegates `split_off()` to `Vec`), and any other
existing use cases.
This change adds tests to validate the behavior of `split_off()` with
regard to capacity, as originally documented, and confirm that behavior
still holds, when `at == 0`.
The change is an implementation detail, and does not require a
documentation change, but documenting the new behavior as part of its
API contract may benefit future users.
(Let me know if I should make that documentation update.)
Note, for future consideration:
I think it would be helpful to introduce an additional method to `Vec`
(if not also to `String`):
```
pub fn take_all(&mut self) -> Self {
self.split_off(0)
}
```
This would make it more clear how `Vec` supports the pattern, and make
it easier to find, since the behavior is similar to other `take()`
methods in the Rust standard library.
This is very similar to the existing `Box<[T; N]>: TryFrom<Box<[T]>>`, but allows avoiding the `shrink_to_fit` if you have a vector and not a boxed slice.