0

I have what seems to me like a simple Itertools::group_by task, but I can't make it compile unless I add clone calls.

use itertools::Itertools; // 0.9.0;
use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Server {
    pub desc: String,
    pub group_name: Option<String>,
}

fn main() {
    let servers = vec![
        Server {
            desc: "one".to_string(),
            group_name: Some("A".to_string()),
        },
        Server {
            desc: "two".to_string(),
            group_name: Some("A".to_string()),
        },
        Server {
            desc: "three".to_string(),
            group_name: None,
        },
    ];

    let mut servers_by_group_name = HashMap::new();
    for (key, group) in &servers.into_iter().group_by(|s| s.group_name) {
        servers_by_group_name.insert(key, group.collect::<Vec<_>>());
    }
}

playground

The error is:

error[E0507]: cannot move out of `s.group_name` which is behind a shared reference
  --> src/main.rs:27:59
   |
27 |     for (key, group) in &servers.into_iter().group_by(|s| s.group_name) {
   |                                                           ^^^^^^^^^^^^
   |                                                           |
   |                                                           move occurs because `s.group_name` has type `std::option::Option<std::string::String>`, which does not implement the `Copy` trait
   |                                                           help: consider borrowing the `Option`'s content: `s.group_name.as_ref()`

The error is quite clear to me. I know I could make it go away by cloning the s.group_name in the group_by. I believe I could also use .iter() instead of .into_iter(), then it would possibly work, but then I would have a Vec<&Server> in the values of my map, but I'd really like a Vec<Server> (because my next step is wrapping that Server in a new datastructure and I really want the ownership).

Is there a way to implement this without cloning the group_name in the group_by closure?

Makyen
  • 31,849
  • 12
  • 86
  • 121
Emmanuel Touzery
  • 9,008
  • 3
  • 65
  • 81
  • 1
    I think the answer is the same core as [Why can't I use a key function that returns a reference when sorting a vector with sort_by_key?](https://stackoverflow.com/q/47121985/155423) – Shepmaster Jun 08 '20 at 18:18
  • 1
    It looks like your question might be answered by the answers of [How to use a struct's member as its own key when inserting the struct into a map without duplicating it?](https://stackoverflow.com/q/41035869/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Jun 08 '20 at 18:19
  • 1
    See also [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/q/32300132/155423) – Shepmaster Jun 08 '20 at 18:20
  • 1
    You could do that https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8c5d94ec0b2d8a2493096a90d3828d80, this can be improve by implementing a iterator specific for this use case if you expect to use this a lot. – Stargateur Jun 08 '20 at 18:35
  • @Shepmaster ok that's a lot of reading. I wouldn't have been able to find the links on my own & be sure they were discussing the same issue! If I understand correctly that the answer is "it's not possible in rust currently with the signature of that itertools function" and "you can roll your own function to do that" [like Stargateur helpfully demonstrated], then I'm ok to close the question as already answered. I cannot be 100% sure myself until I'll read all that material, which will take me some time though. But for now I assume that you're correct in saying that it's a duplicate. – Emmanuel Touzery Jun 08 '20 at 18:57
  • @Stargateur this is very helpful, thank you! – Emmanuel Touzery Jun 08 '20 at 19:16

0 Answers0