Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

vector

rust

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?

like image 412
Pioneer_11 Avatar asked Nov 30 '25 11:11

Pioneer_11


2 Answers

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
    }
}
like image 139
cafce25 Avatar answered Dec 03 '25 01:12

cafce25


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.

like image 24
effect Avatar answered Dec 03 '25 00:12

effect



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!