Repository: rust
Issue: Default derived `Hash` impl for `fn` types can lead to subtle bugs
Labels: T-libs-api,C-bug

<!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
along with any information you feel relevant to replicating the bug.
-->

So I was a bit hesitant to file this bug because the behavior makes sense once you realize what's happening, but I think due to the nature of what can happen it was worth filing an issue. (I briefly searched existing issues and couldn't find anything similar)

Today, `#[derive(Hash)]` is able to derive an implementation for function pointers used as fields in a struct. For example, imagine: 

```rs
use std::hash::{Hash, Hasher};

trait ExampleTrait {
  fn example() -> ();
}

#[derive(Hash)]
struct FunctionWrapper {
  func: fn() -> ()
}

impl FunctionWrapper {
  fn new<T: ExampleTrait>(_: T) -> Self {
    Self {
      func: T::example
    }
  }
  
  fn print_hash(&self) {
      let mut hasher = std::hash::DefaultHasher::new();
      self.hash(&mut hasher);
      eprintln!("{}", hasher.finish());
  }
}

struct Example; 

impl ExampleTrait for Example {
    fn example() -> () {
      ()
    }
}

struct Example2;
impl ExampleTrait for Example2 {
    fn example() -> () {
      ()
    }
}

fn main() {
    let example = Example;
    let example2 = Example2;
    
    let wrapper = FunctionWrapper::new(example);
    let wrapper2 = FunctionWrapper::new(example2);
    
    wrapper.print_hash();
    wrapper2.print_hash();
}
```

Now I would expect that `wrapper.print_hash()` and `wrapper2.print_hash()` print different hashes. This works.
```
   Compiling playground v0.0.1 (/playground)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.49s
     Running `target/debug/playground`
13248834103875764188
6707210748105774930
```

What's unexpected is the second time I run this,

```
   Compiling playground v0.0.1 (/playground)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running `target/debug/playground`
5430550945448582829
17979896429865431302
```

I get different results. When I think about it, this makes sense - you can't make guarantees on where a function pointer is located, and when I look at the `Hash` implementations I'm guessing this ends up falling into the `*const usize` Hash implementation. (I was a bit surprised that there was an implementation for hashing raw pointers to be honest)

It's unexpected because typically when `Hash` provides implementations you half expect them to be semi-stable hashes (at least for the same compiler version), for example you can't derive an implementation for floats because you can't make the same guarantee you get the same float each time (At least that's why I assumed there's no default implementation for f32, f64, etc.) so then you leave it up to the developer to decide they want to handle it.

This would be a hack, but I kind of thought it would essentially end up hashing a string literal like, `<Example as ExampleTrait>::example_ptr_0_0` and `<Example2 as ExampleTrait>::example_ptr_0_0`.

To summarize, I don't think the problem is that the function hashes in an unexpected way, but I do feel like it's an anti-pattern to allow it to be derived in the first place.
 
### Meta
<!--
If you're using the stable version of the compiler, you should also check if the
bug also exists in the beta or nightly versions.
-->

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ea63c430c7c816134027c5ff33c86821

`rustc --version --verbose`:
```
rustc 1.83.0-nightly (9b72238eb 2024-09-14)
binary: rustc
commit-hash: 9b72238eb813e9d06e9e9d270168512fbffd7ee7
commit-date: 2024-09-14
host: x86_64-unknown-linux-gnu
release: 1.83.0-nightly
LLVM version: 19.1.0
```