Skip to content

vec.reserve() unintuitive behavior #23842

Closed
@ghost

Description

There are three issues I see with vec.reserve()

Issue 1: The results can be vastly different for minimally different input vectors. For example this code:

fn main() {
    let mut x = vec![0i8; 1023];
    x.reserve(1);
    let mut y = vec![0i8; 1024];
    y.reserve(1);
    println!("x capacity = {}, y capacity = {}", x.capacity(), y.capacity());
}

prints "x capacity = 1024, y capacity = 2048". The inputs to such a function are very similar, so we should expect reserve() to behave similarly on them, but we see that reserve() assigns special significance to powers of 2 for no reason.

Suggested fix: if we need to increase capacity past the current capacity, simply double the current capacity. So above, our result would be: "x capacity = 2046, y capacity = 2048".


Issue 2: How exactly the vector decides to expand can have huge performance considerations. See for example https://github.com/rust-lang/rust/blob/master/src/libcollections/vec.rs#L536. Also see for example the thread at #23820 (comment). All the API guarantees is that the vector will accommodate at least the size requested. However, in both of these situations, it is critical for performance reasons that the algorithm generally give you more for you ask for, and in the pull request I referenced, if you don't have the doubling behavior, you go from O(n) to O(n^2). It seems like such an important performance consideration should not be hidden in the implementation.

Recommendation: Document in the API that, if reserve() needs to allocate memory less than double its current capacity, it will double its current capacity.


Issue 3: The difference between reserve() and reserve_exact() seem confusing, mostly due to the name of the former. I'm sure there will be plenty of examples that crop up as well of people using reserve() where they meant to use reserve_exact(), or using reserve() and reasoning that it behaves like reserve_exact() (the pull request shows an example of this).

Recommendation: Change the name of reserve() to something more specific, so that people don't get confused and use reserve() where they mean to use reserve_exact(). Something like reserve_round_up().


I'm currently testing a commit that fixes a few issues in vec.rs (involving expansion behavior near usize::MAX). I can start working on fixing issues 1 and 2 now, though it seems like a large-ish API change like issue 3 (and maybe 2) would require an RFC. Let me know if there's enough interest to be worth submitting.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions