Vec::grow_to

Surprised I couldn't find any discussion on this, so feel free to direct me to it if it exists. Is there a specific reason why Vec (and String, VecDeque, etc.) never got a grow_to function? There are good reasons why the reserve function works as it does but I don't think that rules out a grow_to counterpart with different functionality?

I found I've written this a few different times now across various crates and just having it out of the box would be handy:

trait VecExt {
    fn grow_to(&mut self, capacity: usize);
}

impl<T> VecExt for Vec<T> {
    fn grow_to(&mut self, capacity: usize) {
        if self.capacity() < capacity {
            self.reserve(capacity - self.capacity());
        }
    }
}
3 Likes

I think it's just that

if let Some(more) = capacity.checked_sub(vec.capacity()) {
    vec.reserve{_exact}(more);
}

just isn't that big a deal, so it's not obviously worth it to add both a grow_to and grow_to_exact.

2 Likes

I’m not sure if the name grow_to wouldn’t lead in the expectation that the behavior of this function would correspond to reserve_exact rather than reserve.

But it’s also desirable that – for users that don’t want to worry/reason about the distinction, the reserve-like behavior is chosen, because it’s generally less severely negative – generally at worst resulting in a constant factor (2x, I believe) use of extra space when you didn’t need it, but it isn’t turning O(n) times into O(n²) accidentally. So it might be a good idea to keep the same convention that the exact variant gets the longer name.

However shrink_to does have “exact” semantics[1], which means that a choice like grow_to&grow_to_exact would be a bit unsymmetrical, and would make a non-exact grow_to even more surprising. Realistically, ultimately Rust’s Vec is simply inherently a bit asymmetrical – it comes with a geometric growing strategy, but doesn’t offer the same for shrinking (probably for good reason; the use cases where this is worth more than it costs are probably not super common).


I’m now noticing you’re linking a video that already addresses the O(n²) problem of reserve_exact

and you’re talking about “grow_to with different functionality” as if grow_to is supposed to be different from Rust’s reserve in this respect – maybe I’m misunderstanding this sentence though. If I’m not misunderstanding it, I am however confused why you do present a grow_to implemented in terms of reserve instead of reserve_exact. Feel free to elaborate :wink:


  1. shrinking to exactly the desired capacity, or potentially anything up to the minimal capacity larger than that which allocator can offer ↩︎

Oh sorry, to clarify, I mean different functionality in that you're specifying a new maximum size rather than additional capacity bump. In my version I'm still using reserve under the hood to preserve the (desirable) over-estimating behavior. A grow_to_exact could use reserve_exact under the hood as well, and clear up the ambiguity. Still, yes, it is a little inconsistent with shrink_to, but only just I think. The name is bikesheddable certainly.

Or alternatively

vec.reserve(capacity.saturating_sub(vec.capacity()));

I can relate to wanting an API like the proposed Vec::grow though. Most of the time when I use Vec::reserve I know the total lower bound up front, so the provided argument always looks superficially like desired_cap - cap.

2 Likes