How to swap contents or two collection?

Is there a way to move data from on Hashset to the other without cloning it ? Or is there any way to swap already_seen and first_seen ?

fn main() {
    let mut args: Vec<String> = std::env::args().collect();
    let mut already_seen = HashSet::<String>::new();
    let mut first_seen =  HashSet::<String>::new();
    let query = if args.len() > 1 {
        args.pop().unwrap()
    } else {
        String::from("_exists_:message")
    };

    loop {
        let res = get_messages(GRAYLOG_TOKEN, &query, GRAYLOG_DEFAULT_RANGE_SEC);
        for GraylogMessage { message } in res.messages {
            if !already_seen.contains(&message.id) {
                println!(
                    "{} | {} | {} | {}",
                    message.id, message.syslog_level, message.timestamp, message.message
                );
            }
            first_seen.insert(message.id);
        }
        already_seen.clear();
        already_seen.extend(first_seen.clone().into_iter());
        first_seen.clear();
        std::thread::sleep(time::Duration::from_secs(1));
    }
}

You're probably looking for HashSet::drain.

(already_seen, first_seen) = (first_seen, already_seen);
3 Likes

Or simply core::mem::swap(&mut already_seen, &mut first_seen);

3 Likes

what is the difference with (already_seen, first_seen) = (first_seen, already_seen);

Practically, nothing. I think mem::swap() is simpler and communicates the intent more clearly.

No difference here. In general, mem::swap can also work in cases where you only have &mut references to the values in question, whereas (already_seen, first_seen) = (first_seen, already_seen); is restricted to cases where you own already_seen and first_seen, so you can move out of them.

The limitation is a bit arbitrary for this particular case, but in general, it ensures that programs stay well-behaved in the presence of panics. Feel free to click here for more details…

Technically evaluating the right-hand side of (already_seen, first_seen) = (first_seen, already_seen); moves the value out of first_seen and already_seen, leaving them in a de-initialized state, until the assignment itself then re-initializes both values to their new (now swapped) values. If you did this in two steps

let values = (first_seen, already_seen);
(already_seen, first_seen) = values;

then you could start doing something else in the middle

let values = (first_seen, already_seen);
something_else();
(already_seen, first_seen) = values;

and if something_else(); panics, it means that the values will be dropped and first_seen, already_seen both left uninitialized - which is something you can only do if you own them, whereas mutable borrows promise the owner that something will still be there when the borrow ends.

2 Likes

HashSet::insert returns a bool telling you if value is new or already present.