Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Is there a nicer way to create a vector from indexed data in rust?

I have the problem, that I get some data from an iterator (specifically a bevy Query) and part of that data is an index. I now want to create a vector with this data, where every element is placed at the index it is intended to go.

To illustrate the problem, this is how I might solve this in python:

def iterator():
    # The iterator doesn't necessarily give back the data in the correct order
    # However, the indices are always dense from 0...n
    yield ("A", 0)
    yield ("C", 2)
    yield ("B", 1)

def some_computation(x):
    return x + " + some other data"

data_list = [0] * 3 # I know the length
for data, index in iterator():
    data_list[index] = some_computation(data)

print(data_list)

Below is how I currently implemented this in rust, but I’m not super happy about it. Firstly, it takes O(n log n) time.
Secondly, its feels difficult to read, especially the conversion to get rid of the index in the data.
Thirdly, I haven’t yet figured out how to extract this into its own function without violating lifetimes.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Open in the Rust Playground

// I have so far avoided deriving Clone on this struct.
#[derive(Debug)]
struct OwnedDataWithReference<'a> {
    data: &'a str,
}

fn do_something_with_data_list_and_drop_it(x: Vec<OwnedDataWithReference>) {
    println!("{:?}", x);
}

fn main() {
    // In my application this is a bevy query, which can't be indexed.
    let iterator = vec![("A", 0), ("C", 2), ("B", 1)];

    // First the data is created and stored in a vector with a index associated
    // and only subsequently sorted, which costs runtime, but has nice ownership.
    let mut data_list_with_index = Vec::new();
    for (data_ref, index) in iterator.iter() {
        // In my application I first have to fetch data using data_ref
        let new_data = OwnedDataWithReference {
            data: data_ref,
        };
        data_list_with_index.push((new_data, index));
    }
    // This reverse sort and stack based map is needed because I don't want to
    // make `OwnedDataWithReference` Clone. This way, I can transfer ownership.
    data_list_with_index.sort_by_key(|(_, i)| -*i);
    let mut data_list = Vec::new();
    while !data_list_with_index.is_empty() {
        data_list.push(data_list_with_index.pop().unwrap().0);
    }

    // In my application I then serialize this with serde.
    do_something_with_data_list_and_drop_it(data_list);
}

I have tried an approach with using Vec<Option<OwnedDataWithReference>>, but this complained that OwnedDataWithReference is not Clone, which I’ve tried to avoid so far.

Please let me know if you can find a nicer solution to this problem, or if you think I should derive Clone. (The problem is still not solved then, but I think it gets easier)

>Solution :

I have tried an approach with using Vec<Option<OwnedDataWithReference>>, but this complained that OwnedDataWithReference is not Clone, which I’ve tried to avoid so far.

You probably tried creating a Vec of None values using the vec! macro which requires T: Clone. You can instead create it using another way, for example (0..n).map(|_| None).collect(). With that, this can be done like this:

#[derive(Debug)]
struct OwnedDataWithReference<'a> {
    data: &'a str,
    other_stuff: u32,
}

fn do_something_with_data_list_and_drop_it(x: Vec<OwnedDataWithReference>) {
    println!("{:?}", x);
}

fn main() {
    let iterator = vec![("A", 0), ("C", 2), ("B", 1)];

    let mut data_list = (0..iterator.len()).map(|_| None).collect::<Vec<_>>();

    for (data_ref, index) in iterator.iter() {
        let new_data = OwnedDataWithReference {
            data: data_ref,
            other_stuff: 42,
        };
        data_list[*index] = Some(new_data);
    }
    
    // This will panic if any element is still `None` at this point.
    let data_list = data_list.into_iter().map(Option::unwrap).collect();
    
    do_something_with_data_list_and_drop_it(data_list);
}

Playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading