Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a vector with non-constant length

Tags:

rust

Editor's note: this question was asked before Rust 1.0 and some of the assertions in the question are not necessarily true in Rust 1.0. Some answers have been updated to address both versions.

I want to create a vector, but I only know the size I want the vector to be at runtime. This is how I'm doing it now (i.e. creating an empty, mutable vector, and adding vectors to it) :

fn add_pairs(pairs: ~[int]) -> ~[int] {
    let mut result : ~[int] = ~[];
    let mut i = 0;
    while i < pairs.len() {
        result += ~[pairs[i] + pairs[i + 1]];
        i += 2;
    }
    return result;
}

This is how I want to do it (i.e., creating a vector and putting everything in it, instead of adding lots of vectors together):

fn add_pairs(pairs: ~[int]) -> ~[int] {
    let number_of_pairs = pairs.len() / 2;
    let result : ~[int, ..number_of_pairs];
    let mut i = 0;
    while i < pairs.len() {
        result[i] = pairs[2 * i] + pairs[2 * i + 1];
        i += 1;
    }
    return result;
}

Unfortunately, doing the above gives me something like:

error: expected constant expr for vector length: Non-constant path in constant expr
let result: ~[int, ..number_of_pairs];
             ^~~~~~~~~~~~~~~~~~~~~~~~

I get the impression that vectors have to have their size known at compile time (and so you need to set their size to a constant). Coming from a Java background, I'm confused! Is there a way to create a vector whose size you only know at runtime?

I'm using Rust 0.6.

like image 545
Daniel Avatar asked May 25 '13 02:05

Daniel


2 Answers

In Rust version 1.0.0, they've made the std::vec:Vec public structure stable so that you can instantiate a growable vector with let mut my_vec = Vec::new(); You can also use the vec! macro like so: let mut another_vec = vec![1isize, 2isize, 3isize]; What is important to note is that in both cases the variable you're assigning must be mutable.

With these vectors you can call my_vec.push(num); for individual items or another_vec.extend_from_slice(["list", "of", "objects"]); to add items to the end of the vector.

For your specific problem, you could do something like this:

fn add_pairs(pairs: Vec<(Vec<isize>)>) -> Vec<isize> {
    let mut result = Vec::new();
    for pair in pairs.iter() {
        result.push(pair[0]);
        result.push(pair[1]);
    }
    return result;
}

You can see this in action on the Rust Playground where you have (what I assumed) was a nested vector of integer pairs.

like image 189
rouma7 Avatar answered Oct 10 '22 20:10

rouma7


There is no way to create an array of constant length with the length determined at runtime; only compile-time constant length arrays are allowed, so (variations of) your first method with Vec<i32> (previously ~[int]) is the only supported way. You could use vec![0; number_of_pairs] to create a vector of the correct size and use the second part.


There are many helper functions for what you are trying to do (using while directly Rust should be very rare):

fn add_pairs(pairs: &[i32]) -> Vec<i32> {
    let mut result = Vec::new();
    for i in 0..(pairs.len() / 2) {
        result.push(pairs[2 * i] + pairs[2 * i + 1])
    }
    result
}

Or even

fn add_pairs(pairs: &[i32]) -> Vec<i32> {
    pairs
        .chunks(2)
        .filter(|x| x.len() == 2)
        .map(|x| x[0] + x[1])
        .collect()
}

Docs: chunks, filter, map, collect. (The filter is just because the last element of chunks may have length 1.)

Also note that adding two vectors allocates a whole new one, while push doesn't do this necessarily and is much faster (and .collect is similar).

like image 40
huon Avatar answered Oct 10 '22 20:10

huon