PhantomData<fn() -> H> pattern in BuildHasherDefault source

Source of BuildHasherDefault is quite simple:

pub struct BuildHasherDefault<H>(marker::PhantomData<fn() -> H>);

Usage of PhandomData here is pretty clear, H is unused in struct definition and PhandomData is here to make compiler happy.

Also, usage of fn() -> H is probably to let readers know that the values of type H are only produced (not owned) in BuildHasherDefault implementations. Is there any other reason to use fn() -> H here apart from documentation purposes?
I read Table of PhantomData patterns, but it doesn't tell much why I could use one, not another.

fn() -> H is always Send and Sync no matter what the type of H is.

5 Likes

Good catch! That makes whole BuildHasherDefault both Send and Sync no matter what H is.

Illustration:

use std::marker::PhantomData;
use std::rc::Rc;

fn main() {
    //Rc<_> is !Send !Sync
    let with_fn: PhandomDataWithFn<Rc<i32>> = PhandomDataWithFn(PhantomData);
    let with_t: PhandomDataWithT<Rc<i32>> = PhandomDataWithT(PhantomData);
    needs_send_sync(with_fn); // does compile
    needs_send_sync(with_t); //<- doesn't compile
}

fn needs_send_sync(arg: impl Send + Sync) {
    //...
}

struct PhandomDataWithFn<T>(PhantomData<fn() -> T>);

struct PhandomDataWithT<T>(PhantomData<T>);
1 Like

You might find the updated table of the nightly version of the book handy:

1 Like