PhantomData
is super useful from typesystem perspective, but PhantomData
values are usually uninteresting, and explicit initialization of phantom fields in struct literals is just boilerplate:
StructWithPhantom {
value,
_phantom_field: PhantomData, // this uses type inference anyway
}
The field has to be explicitly initialized, but this explicitness doesn't add new information, because the generic types are usually specified elsewhere. This can be even seen as leaking an implementation detail, because other ways of defining generic types don't need a faux value.
A struct with public fields can't add a generic type requiring PhantomData
without a semver break, even if the type argument has a default (Foo
-> Foo<T = ()>
).
A single-field tuple struct works nicely with callbacks like map
: value.map(Wrapper)
, but adding a PhantomData
makes it clunky value.map(|v| Wrapper(v, PhantomData))
and IMHO this just takes away convenience, without adding clarity (selection of the type is delegated to type inference anyway).
Rust hasn't figured out delegation yet, or fields in traits, so a phantom type argument could be an easy way of creating "newtypes" of POD structs like Point
or RGB
:
struct Point<Domain> {
x: i32, y: i32,
_domain: PhantomData<Domain>,
}
so you could have type-safe Point<WorldSpace>
and Point<ScreenSpace>
, and implement methods on Point<T>
without extra hoops of traits without fields, or enormous boilerplate of properly delegating all impls from some WorldSpacePoint(Point)
to Point
without a type-safety-defeating Deref
cop-out.
but it doesn't work with the simple Point { x, y }
syntax, and explicit management of the extra field gets tiring quickly.
The extra field also gets in the way when destructuring and matching against the type.
So it makes me wonder what Rust could do to make PhantomData
less burdensome?
Can type arguments get an attribute that makes them used in structs, without this being an explicit field?
struct Struct<#[phantom(&mut T)] T> {}
Or maybe there could be a feature that is not phantom-data specific, but helps initialize such fields in general?
struct Struct<T> {
#[default]
// gets initialized even without `{ ..Default::default() }`
_phantom: PhantomData<&mut T>,
}