Does memory copy occur when hashmap.insert()

// Assume that ExternStruct{} and fn foo() {} are defined in an external crate as follows:

#[derive(Copy, Clone, Debug)]
struct ExternStruct {
     v0: i32,
     v1: u32,
     v2: i64,
     v3: u64,
}

static ok:bool = true;
use std::io::{Error, ErrorKind };
fn foo() -> io::Result<ExternStruct> {
    if ok {
        Ok(ExternStruct { v0: 1, v1:2, v2: 3, v3: 4 })
    } else {
        Err(Error::new(ErrorKind::Other, "oh no!"))
    }   
}
//*****************************************************************************

// And, my code calls foo() many times, and saves the return value into a hashmap as follows:

 1 #[derive(Debug)]
  2 struct MyStruct {
  3     exstruct: ExternStruct,
  4     count: i32,
  5     other: u32
  6 }
  7 
  8 use std::io;
  9 use std::collections::HashMap;
 10 fn main() {
 11     let mut s_map = HashMap::new();
 12     let mut key = 0i32;
 13 
 14     for i in 0..300 {
 15         key += 1;
 16         match foo() {
 17             Ok(s_ex) => {
 18                let m =  MyStruct { 
 19                    exstruct: s_ex, 
 20                    count: 1, 
 21                    other: 2
 22                };
 23                s_map.insert(key, m);
 24             },
 25             Err(_) => println!("foo return error")
 26         }
 27     }
 28     
 29     println!("s_map: {:?}", s_map);
 30 }

My questions are :

  1. In Line 18, when the variable m is created, memory copy (exstruct <== s_ex) will happen, Right ?
    how to avoid the memory copy here ?

  2. In Line 23 s_map.insert(key, m), HashMap.insert() will use memory Copy, right ?

Please use code blocks:

```
// your code here
```

As for your memory copies, that's up to the compiler. There may be some copying involved and there may not. Why are you worrying about these copies?

Thank u, just add "```" to wrap the code block :slight_smile:

memory copy will have some effects on performance. Just want to avoid unnecessary memcpy.

I would classify both of those as "moves", not "Copy", but besides that you're correct. The compiler needs to move the values around.

However, in Rust, moves (and usages of Copy) are guaranteed to always be a pure byte-for-byte copy and nothing else, so they're always very fast. (If you know C++, then you might think of Move constructors and Copy constructors - Rust has neither of these).

The compiler will usually optimize them all out, but even if it didn't, it would be almost instantaneous. Unless you're storing a ton of data in a struct (maybe 256+ bytes?) AND your code is a hot path in a loop, it's very rarely worth avoiding - trying to avoid moves of small structs like this is the kind of micro-optimization which is almost never worth anything.

If you do want to avoid copying the full data, then you can always surround the structure in a Box. It'll be moved onto the heap once, and then you'll just be copying around a ptr to the heap. But unless you have a ton of data and move it around a ton without accessing it, this will just make everything significantly slower.

It's also not the case that you can always avoid copying (even by optimization). If, for example, inlining didn't happen because there is a dynamic call through a function pointer, then the value cannot just be placed/instantiated directly at the address the function will want to move it to. Instead it has to go through the ABI-specific process for argument passing, which might include moving parts of a struct into registers, or pushing the entire thing on the stack before the call.

In general, just don't worry about moves. The optimizer will remove excess copying if it can, and you can't do any better if it's not possible.

4 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.