Skip to content

Compiler unhelpful when clone surprisingly returns shared reference instead of owned object #109429

Closed
@Zannick

Description

@Zannick

I tried this code, mimicking a usage of fasthash that I expected to work with HashMap:

use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::BuildHasher;
use std::hash::Hash;

pub struct Hash128_1;

impl BuildHasher for Hash128_1 {
    type Hasher = DefaultHasher;
    fn build_hasher(&self) -> DefaultHasher { DefaultHasher::default() }
}

#[allow(unused)]
pub fn hashmap_copy<T, U>(
    map: &HashMap<T, U, Hash128_1>,
) where T: Hash + Clone, U: Clone
{
    let mut copy: Vec<U> = map.clone().into_values().collect();
}

pub fn make_map() -> HashMap<String, i64, Hash128_1>
{
    HashMap::with_hasher(Hash128_1)
}

I expected to see one of these happen:

  • hashmap_copy compiles, so function can copy the hashmap values into a vector and modify it as necessary.
  • rustc gives an error that Clone is not implemented for HashMap<T, U, Hash128_1> because Hash128_1 does not have an implementation for Clone.

Instead, this happened:

error[[E0507]](https://doc.rust-lang.org/nightly/error_codes/E0507.html): cannot move out of a shared reference
  --> src/lib.rs:19:28
   |
19 |     let mut copy: Vec<U> = map.clone().into_values().collect();
   |                            ^^^^^^^^^^^^-------------
   |                            |           |
   |                            |           value moved due to this method call
   |                            move occurs because value has type `HashMap<T, U, Hash128_1>`, which does not implement the `Copy` trait
   |
note: `HashMap::<K, V, S>::into_values` takes ownership of the receiver `self`, which moves value
  --> /rustc/44f5180584404d18058cbbf224c55255db4fdcbb/library/std/src/collections/hash/map.rs:487:24

This appears to be a complaint that map.clone() is already borrowed somehow by the time I call into_values on it. On further research, there is a Clone implementation for shared references (#91805). I can get around this with map.values().map(Clone::clone).collect() to avoid needing Clone on the hasher param but:

  1. I find it surprising that clone() can return a HashMap reference instead of an owned object based on whether I messed up the type parameters.
  2. The error message was perplexing and I got lucky with my research while filing this bug report.

Built on 1.68.0 stable and 1.70.0-nightly in the playground (2023-03-20 44f5180)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions