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

why rust into_iter move whole collection instead of clean it

Example scenario

struct Foo {
    bar: Vec<i32>,
}

let foo = Foo { bar: vec![1, 2, 3] };
let bar: HashMap<i32, i32> = foo.bar.into_iter().map(|x|(x,x)).collect();

Expected behaviour: all values from foo.bar moved into new hash map, foo.bar now is clean vec and can be used after this, foo as whole struct can be used after this.

Real behaviour: foo.bar moved as whole collection, so whole struct foo now is partial moved and cant be used.

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

Question: why into_iter consume whole collection instead of consuming only elements and leave struct empty and how I can workaround this ?

>Solution :

Why calling into_iter on Vec<T> consumes vector?

This is because IntoIterator trait is defined to consume value turned into iterator (by convention "into" in Rust means that you change something into something else, if you only want to view/borrow something, usually you will see "as"; for example Vec::as_slice).

pub trait IntoIterator {
    type Item;
    type IntoIter: Iterator<Item = Self::Item>;

    // Required method
    fn into_iter(self) -> Self::IntoIter;
}

Vec::into_iter essentially uses this trait, so it only makes sense that it consumes vector.

How to "take" some value and leave an empty value in its place to avoid partial move?

You can use std::mem::take, which is defined as:

pub fn take<T>(dest: &mut T) -> T
where
    T: Default,

It takes value from &mut T and uses trait Default to construct a default (usually empty in case of collections) object in its place. Since Vec implements Default you can use it as follows:

struct Foo {
    bar: Vec<i32>,
}

fn main() {
    let mut foo = Foo { bar: vec![1, 2, 3] };
    // Take foo.bar so it can be consumed later, and leave an empty vector in its place.
    // Note that this requires mutating foo.
    let bar = std::mem::take(&mut foo.bar);
    let bar: std::collections::HashMap<i32, i32> = bar.into_iter().map(|x| (x, x)).collect();
    println!("{bar:?}");
}

In general if you want to take some value, which does not implement Default trait, or you want to use different value than it provides, you can use std::mem::replace or std::mem::swap.

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