brundolf
brundolf

Reputation: 1490

Why doesn't clone() allow for this move?

I don't understand why the following doesn't work:

use std::collections::HashMap;

#[derive(Debug,Clone,PartialEq)]
struct Foo<'a> {
    contents: HashMap<&'a str, Foo<'a>>,
}

fn bar<'a>(val: Foo<'a>) -> Foo<'a> {
    *val.contents.get("bar").clone().unwrap()
}
error[E0507]: cannot move out of a shared reference
 --> src/lib.rs:9:5
  |
9 |     *val.contents.get("bar").clone().unwrap()
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Foo<'_>`, which does not implement the `Copy` trait

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8ab3c7355903fc34751d5bd5360bb71a

I'm performing a .clone(), which I thought should allow me to return the resulting value whose ownership should be disentangled from the input value, but that doesn't appear to be the case.

Another weird thing, which isn't a blocker on its own but may hint at the underlying problem, is that for some reason the .clone() returns a &Foo, which is surprising because in other cases I've mostly seen .clone() go from &T -> T. This is why the * is there; without it this doesn't pass type checking. I know Rust has some "magical" referencing/dereferencing rules, but I can't quite figure this one out.

Upvotes: 2

Views: 592

Answers (1)

Michael Anderson
Michael Anderson

Reputation: 73470

HashMap.get returns an Option<&T>. Cloning this option gives you another Option<&T>, referencing the same object.

If you want to convert a Option<&T> to a new Option<T> where T supports Clone you can use .cloned(). There's also no need to dereference anymore as you have a T not an &T.

This means your code will look like this:

use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq)]
struct Foo<'a> {
    contents: HashMap<&'a str, Foo<'a>>,
}

fn bar<'a>(val: Foo<'a>) -> Foo<'a> {
    val.contents.get("bar").cloned().unwrap()
}

Upvotes: 2

Related Questions