Description
use core::hash::BuildHasher;
use std::{collections::HashSet, hash::DefaultHasher};
/// no Default trait
struct DummyBuildHasher;
impl BuildHasher for DummyBuildHasher {
type Hasher = DefaultHasher;
fn build_hasher(&self) -> Self::Hasher {
unreachable!();
}
}
/// requires Default trait
trait RealHasher: Sized + BuildHasher + Default {}
impl<T: BuildHasher + Default> RealHasher for T {}
struct Builder<H: BuildHasher> {
languages: Option<HashSet<usize, H>>,
}
impl Builder<DummyBuildHasher> {
fn new() -> Self {
Self { languages: None }
}
// candidate 1
fn build(self) -> Builder<std::hash::RandomState> {
self.languages([].into_iter().collect())
}
}
impl<H: RealHasher> Builder<H> {
// candidate 2
fn build(self) -> Builder<H> {
self
}
}
impl<H: BuildHasher> Builder<H> {
fn languages<H2: RealHasher>(self, languages: HashSet<usize, H2>) -> Builder<H2> {
Builder {
languages: Some(languages),
}
}
fn all_languages<H2: RealHasher>(self) -> Builder<H2> {
self.languages([].into_iter().collect())
}
}
fn main() {
// works
let builder: Builder<std::hash::RandomState> = Builder::new().build();
let builder = Builder::new().languages(HashSet::new()).build();
let builder = Builder::new()
.all_languages::<std::hash::RandomState>()
.build();
// does not compile
let builder: Builder<std::hash::RandomState> = Builder::new().all_languages().build();
}
I expected that all_languages()
filters type by Builder<impl RealHasher>
, and so there would be only one candidate of build()
(second), because hasher of the first one does not implement RealHasher
(because DummyBuildHasher
does not implement Default
), so it would compile.
Instead, it ignores that all_languages()
limits hasher type with trait RealHasher
, and thinks that there are two candidates of build()
.
Ideas
I still don't understand why it enables the second candidate of build()
, but does not disable the first one. Because without all_languages()
there is only one candidate. Maybe it correctly adds the trait RealHasher
, but forgets to remove the first candidate of build()
from a list of all possible candidates?
Also given that .all_languages::<std::hash::RandomState>().build()
works, but not .all_languages().build::<std::hash::RandomState>()
, maybe there is a problem with type determination (passing) in chained calls?
Meta
rustc --version --verbose
:
rustc 1.88.0-nightly (e9f8103f9 2025-05-07)
binary: rustc
commit-hash: e9f8103f93f8ce2fa2c15c0c6796ec821f8ae15d
commit-date: 2025-05-07
host: x86_64-unknown-linux-gnu
release: 1.88.0-nightly
LLVM version: 20.1.4
Backtrace
error[E0034]: multiple applicable items in scope
--> src/main.rs:62:83
|
62 | let builder: Builder<std::hash::RandomState> = Builder::new().all_languages().build();
| ^^^^^ multiple `build` found
|
note: candidate #1 is defined in an impl for the type `Builder<DummyBuildHasher>`
--> src/main.rs:27:5
|
27 | fn build(self) -> Builder<std::hash::RandomState> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl for the type `Builder<H>`
--> src/main.rs:34:5
|
34 | fn build(self) -> Builder<H> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^