Pioneer_11
Pioneer_11

Reputation: 1280

How to panic if a vector's capacity is increased?

I am working on implementing a sieve of Atkin which takes a number and returns a vector of all primes below that number. There are two different vectors I need use this function.

  1. BitVec 1 for prime 0 for not prime (flipped back and forth as part of the algorithm).
  2. Vector containing all known primes.

The size of the BitVec is known as soon as the function is called. While the final size of the vector containing all known primes is not known, there are relatively accurate upper limits for the number of primes in a range. Using these I can set the size of the vector to an upper bound then shrink_to_fit it before returning. The upshot of this neither array should ever need to have it's capacity increased while the algorithm is running, and if this happens something has gone horribly wrong with the algorithm.

Therefore, I would like my function to panic! if the capacity of either the vector or the bitvec is changed during the running of the function. Is this possible and if so how would I be best off implementing it?

Upvotes: 3

Views: 698

Answers (2)

effect
effect

Reputation: 1455

An alternative to the newtype pattern is to create a new trait with a method that performs the check, and implement it for the vector like so:

trait PushCheck<T> {
    fn push_with_check(&mut self, value: T);
}

impl<T> PushCheck<T> for std::vec::Vec<T> {
    fn push_with_check(&mut self, value: T) {
        let prev_capacity = self.capacity();
        self.push(value);
        assert!(prev_capacity == self.capacity());
    }
}

fn main() {
    let mut v = Vec::new();
    v.reserve(4);
    dbg!(v.capacity());
    
    v.push_with_check(1);
    v.push_with_check(1);
    v.push_with_check(1);
    v.push_with_check(1);
    
    // This push will panic
    v.push_with_check(1);
}

The upside is that you aren't creating a new type, but the obvious downside is you need to remember to use the newly defined method.

Upvotes: 3

cafce25
cafce25

Reputation: 27537

You can assert that the vecs capacity() and len() are different before each push:

assert_ne!(v.capacity(), v.len());
v.push(value);

If you want it done automatically you'd have to wrap your vec in a newtype:

struct FixedSizeVec<T>(Vec<T>);
impl<T> FixedSizeVec<T> {
    pub fn push(&mut self, value: T) {
        assert_ne!(self.0.len(), self.0.capacity())
        self.0.push(value)
    }
}

To save on forwarding unchanged methods you can impl Deref(Mut) for your newtype.

use std::ops::{Deref, DerefMut};
impl<T> Deref for FixedSizeVec<T> {
    type Target = Vec<T>;
    fn deref(&self) -> &Vec<T> {
        &self.0
    }
}
impl<T> DerefMut for FixedSizeVec<T> {
    fn deref_mut(&mut self) -> &mut Vec<T> {
        &mut self.0
    }
}

Upvotes: 4

Related Questions