# PhantomData without field syntax noise

**URL:** <https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062>\
**Category:** language design\
**Created:** [June 21, 2024, 5:05pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062 "2024-06-21T17:05:02Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [June 21, 2024, 5:05pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/1 "2024-06-21T17:05:02Z")

</div>

`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:

```rust
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`:

```rust
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?

```rust
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?

```rust
struct Struct<T> {
   #[default] 
   // gets initialized even without `{ ..Default::default() }`
   _phantom: PhantomData<&mut T>, 
}

```

---

<div class="post-metadata">

**Author:** ![dlight](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dlight/32/8462_2.png) [@dlight](https://internals.rust-lang.org/u/dlight)\
**Post date:** [June 21, 2024, 5:28pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/2 "2024-06-21T17:28:16Z")

</div>

> [@kornel](#):
>
> 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`:
> 
> ```rust
> 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.

This would make me happier if each Point didn't generate a new monomorphization though - at least for the cases where you don't call `T::something()` or things like that in the methods

(or can rustc reliably deduplicate them already?)

---

<div class="post-metadata">

**Author:** ![jdahlstrom](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jdahlstrom/32/3351_2.png) [@jdahlstrom](https://internals.rust-lang.org/u/jdahlstrom)\
**Post date:** [June 21, 2024, 7:03pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/3 "2024-06-21T19:03:03Z")

</div>

Any solution to this should also address destructuring. I have a vector type containing a PD for type-tagging, and while initialization using initializer syntax is ugly as hell, I’ve worked around that by simply providing free functions such as `vec3(f32, f32, f32)`. But there’s no such workaround for destructuring. Luckily handling the PD field doesn’t require as much ceremony as construction does, but still, it’s a small but annoying papercut.

---

<div class="post-metadata">

**Author:** ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)\
**Post date:** [June 21, 2024, 7:25pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/4 "2024-06-21T19:25:47Z")

</div>

> [@kornel](#):
>
> ```rust
> struct Struct<T> {
> #[default] 
> // gets initialized even without `{ ..Default::default() }`
> _phantom: PhantomData<&mut T>, 
> }
> 
> ```

How about instead of baking in `Default`, providing default values (that must be constants)? That way, it's still usable in const eval, and usable in non-ZST situations where a singular default isn't the right .

```rust
struct Struct<T> {
   _phantom: PhantomData<&mut T> = const { PhantomData }, 
}

```

(The `const` block is to mandate and clarify that no side effects will occur from constructing the value.)

That said, this won't help with destructuring at all, which IMO is the worse half of the problem, since you can't have helper functions for patterns (though you could write a helper _macro_). In order to help with destructuring, you'd need some way to express “this field may be ignored, even in an exhaustive struct pattern”. I haven't got any ideas for that.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [June 21, 2024, 7:35pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/5 "2024-06-21T19:35:49Z")

</div>

> [@kornel](#):
>
> Can type arguments get an attribute that makes them used in structs, without this being an explicit field?
> 
> `struct Struct<#[phantom(&mut T)] T> {}`

I would love to see something along these lines. I don't know exactly what it should look like, and I don't know if it should take a type like the `&mut T` here or not, but I think we should have something better than the current solution.

Rationale for potentially not needing to include the specific type containing the phantom type parameter: AFAICT that's primarily used to get properties that would be gained by having a field of that type, such as "I don't want to be `Sync`" or "I want `T: 'a`" or similar, but such properties seem better expressed with things like `impl !Sync for MyType` or `T: 'a`. Given the ability to do something like that, _perhaps_ we just need `struct Struct<phantom T>` or similar.

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jrose/32/9591_2.png) [@jrose](https://internals.rust-lang.org/u/jrose)\
**Post date:** [June 21, 2024, 7:37pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/6 "2024-06-21T19:37:36Z")

</div>

Half-joking, half-serious: allow fields to be marked as `non_exhaustive`, with sort of the opposite meaning as for structs. This definitely won’t be abused by anyone ever, or result in an arms race of exhaustivity checks and opt-outs. (Maybe limit it to zero-sized types.)

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [June 21, 2024, 7:46pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/7 "2024-06-21T19:46:34Z")

</div>

> [@kpreid](#):
>
> ```rust
> struct Struct<T> {
> _phantom: PhantomData<&mut T> = const { PhantomData }, 
> }
> 
> ```

Obligatory mention of this, which I still want as-written:

> [@Pre-pre-RFC: syntactic sugar for \`Default::default()\`](https://internals.rust-lang.org/t/pre-pre-rfc-syntactic-sugar-for-default-default/13234/75):
>
> Thanks @spacekookie! I know is a long thread so it is a good time to summarize what the RFC I'll be writing will propose, particularly because it is removed from what I originally wrote: Language support for default values on structs through const expressions: struct Car { wheel\_count: usize = 4; } Language support for partially defaulted structs: struct Person { name: String, age: i128 = 0; } Teach #[derive(Default)] to use the default values #[derive(Default)] struct P…

(So it'd allow exactly what you wrote, minus the `const` block, since all field defaults would be `const`.)

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [June 21, 2024, 10:42pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/8 "2024-06-21T22:42:23Z")

</div>

> [@kornel](#):
>
> 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 = ()>`).

That already doesn't work. Defaults are ignored by type inference. That is also why `HashMap::new()` is forced to only work with the default hasher. Otherwise type inference wouldn't know which hasher to pick.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [June 21, 2024, 10:54pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/9 "2024-06-21T22:54:23Z")

</div>

👍. It'd be nice if we had working default type parameter fallback.

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [June 22, 2024, 12:57am UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/10 "2024-06-22T00:57:26Z")

</div>

> [@kpreid](#):
>
> `_phantom: PhantomData<&mut T> = const { PhantomData },`

That's cool.

> [@dlight](#):
>
> This would make me happier if each Point didn't generate a new monomorphization though

Monomorphisation bloat is a problem in general, even for non-phantom types (e.g. `vec.len()`), so it may get tackled eventually. LLVM can dedupe identical functions, although that doesn't help compile times

> [@josh](#):
>
> potentially not needing to include the specific type containing the phantom type parameter

`impl !Sync` would be nice, but for other things `PhantomData` is IMHO fine. I remember early Rust had types like `PhantomCovariant` and `PhantomContravariant`, and I needed to re-read the whole Wikipedia article about variance twice to figure out which one is which. Having an "as-if" type is a clever solution to this.

---

<div class="post-metadata">

**Author:** ![jhpratt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jhpratt/32/11640_2.png) [@jhpratt](https://internals.rust-lang.org/u/jhpratt)\
**Post date:** [June 22, 2024, 5:00am UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/11 "2024-06-22T05:00:32Z")

</div>

> [@kornel](#):
>
> Or maybe there could be a feature that is not phantom-data specific, but helps initialize such fields in general?

See [this IRLO thread](https://internals.rust-lang.org/t/pre-rfc-user-provided-default-field-values/15877) where I propose exactly this. I never got around to fully rewriting the RFC, but it is something I still want to do eventually. Essentially you'd have to explicitly declare the default in the field definition and then use `..` to include the field in the struct expression.

---

<div class="post-metadata">

**Author:** ![pacak](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/pacak/32/11934_2.png) [@pacak](https://internals.rust-lang.org/u/pacak)\
**Post date:** [June 22, 2024, 10:44am UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/12 "2024-06-22T10:44:39Z")

</div>

> [@kornel](#):
>
> Can type arguments get an attribute that makes them used in structs, without this being an explicit field?
> 
> `struct Struct<#[phantom(&mut T)] T> {}`

This is similar to how Haskell does things (but with even less syntax - just `struct Struct<T> {}`), I would love to be able to do it in Rust as well, at least as long as I'm not trying to do fancy variance.

---

<div class="post-metadata">

**Author:** ![dlight](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dlight/32/8462_2.png) [@dlight](https://internals.rust-lang.org/u/dlight)\
**Post date:** [June 25, 2024, 5:56am UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/13 "2024-06-25T05:56:02Z")

</div>

Why not use the same mechanism used for `{integer}`, rather than ignoring defaults? (An integer literal whose type fails to be inferred defaults to `i32`)

---

<div class="post-metadata">

**Author:** ![lordan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/lordan/32/2107_2.png) [@lordan](https://internals.rust-lang.org/u/lordan)\
**Post date:** [June 25, 2024, 11:27am UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/14 "2024-06-25T11:27:33Z")

</div>

Could this perhaps be handled via associated types on structs? (FWIW coming from C++ I wanted to have those a couple of times regardless.) For the purpose of phantom data they'd likely need additional annotation, e.g.:

```rust
struct Struct<'a, T> {
    #[phantom_data] // associated type used like PhantomData
    type ReferenceType = &'a T; // access pattern

    index: usize,
    ...
}

```

---

<div class="post-metadata">

**Author:** ![tesaguri](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tesaguri/32/6025_2.png) [@tesaguri](https://internals.rust-lang.org/u/tesaguri)\
**Post date:** [June 26, 2024, 11:29am UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/15 "2024-06-26T11:29:13Z")

</div>

Prior art:

> <https://github.com/rust-lang/rfcs/pull/2357>
>
> \### \[🖼️ Rendered\](https://github.com/Centril/rfcs/blob/rfc/ghost-busting/text/00…00-ghost-busting.md)
> 
> \### 📝 Summary
> 
> \[\`PhantomData\<T\>\`\]: https://doc.rust-lang.org/nightly/nomicon/phantom-data.html
> \[variance\]: https://doc.rust-lang.org/nightly/nomicon/subtyping.html
> \[drop checking\]: https://doc.rust-lang.org/nightly/nomicon/dropck.html
> 
> 1. \*\*Defines unused type parameters to act as if they are logically owned and covariant.\*\*
> 
> 2. \*\*Introduces \`phantom T\` pseudo-fields which improves the ergonomics of changing
> \[variance\] and \[drop checking\] behavior when it is needed.\*\* A \`phantom T\` field also has
> the same behavior with respect to auto traits as \[\`PhantomData\<T\>\`\] does.
> 
> 3. \[\`PhantomData\<T\>\`\] is redefined as \`struct PhantomData\<T: ?Sized\>;\` and deprecated.
> 
> 4. The lang item \`phantom\_data\` is removed.
> 
> 5. Derive macros for derivable standard library traits will take advantage of statically known
> phantom types and fields to generate more permissive \`impl\`s.
> 
> That's it folks; Happy 👻👤!
> 
> \### 💖 Thanks
> 
> Thanks to @nikomatsakis and @eddyb for interesting discussions.

The RFC mentions features similar to the ones proposed in the OP as alternatives.

---

<div class="post-metadata">

**Author:** ![pacak](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/pacak/32/11934_2.png) [@pacak](https://internals.rust-lang.org/u/pacak)\
**Post date:** [June 26, 2024, 11:30am UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/16 "2024-06-26T11:30:17Z")

</div>

Biggest downside I see is that everybody who wants to implement a derive proc macro will have to deal with those extra annotations in some way. Derive macro situation is already miserable as is.

---

<div class="post-metadata">

**Author:** ![maia-s](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/maia-s/32/11597_2.png) [@maia-s](https://internals.rust-lang.org/u/maia-s)\
**Post date:** [June 26, 2024, 2:01pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/17 "2024-06-26T14:01:48Z")

</div>

Something like `struct MyStruct<T: Covariant>();` would be nice. Having to add actual fields just to specify variance is very inconvenient and can require significant changes to the source only for the purpose of being able to implement some trait for your type.

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [June 26, 2024, 2:23pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/18 "2024-06-26T14:23:08Z")

</div>

I agree with the RFC that [variance by example is easier to understand and more flexible](https://github.com/Centril/rfcs/blob/rfc/ghost-busting/text/0000-ghost-busting.md#alternative-explicit-variance-annotations).

---

<div class="post-metadata">

**Author:** ![ds84182](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ds84182/32/12379_2.png) [@ds84182](https://internals.rust-lang.org/u/ds84182)\
**Post date:** [July 27, 2024, 9:14pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/19 "2024-07-27T21:14:42Z")

</div>

I've been thinking about this a little bit. I think it would be better to represent these as "ignored" fields, like:

```rust
struct MyStruct<T> {
    pub real_field: usize,
    _: &mut T, // Phantom Field
    _: T, // Another phantom field!
}

```

I use marker types to represent different transfer functions for an RGB struct. Right now I have type aliases for transfer functions, but they cannot be constructed without specifying the transfer function field.

An alternative that'll work just as well is field defaults. e.g.

```rust
trait MyTrait {
  const DEFAULT: Self;
}

struct MyStruct<T: MyTrait> {
  pub required_field: f32,
  pub defaulted_field: T = <T as MyTrait>::DEFAULT,
}

// ...
let whatever = MyStruct::<MyTraitImpl> { required_field: 100.0 };

```

---

<div class="post-metadata">

**Author:** ![ZyX-I](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/zyx-i/32/11398_2.png) [@ZyX-I](https://internals.rust-lang.org/u/ZyX-I)\
**Post date:** [August 3, 2024, 4:41pm UTC](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062/20 "2024-08-03T16:41:21Z")

</div>

> [@ds84182](#):
>
> I've been thinking about this a little bit. I think it would be better to represent these as "ignored" fields, like:
> 
> ```rust
> struct MyStruct<T> {
> pub real_field: usize,
> _: &mut T, // Phantom Field
> _: T, // Another phantom field!
> }
> 
> ```

This looks like struct containing fields of type `&mut T` and `T` which you cannot name, not PhantomData fields. Though this led me to a different idea: how about adding something like `UnnameableField` trait like the following:

```rust
use core::marker::PhantomData;
trait UnnameableField {
    const VALUE: Self;
}

impl<T> UnnameableField for PhantomData<T> {
    const VALUE: Self = PhantomData;
}

impl UnnameableField for u8 {
    const VALUE: Self = 0;
}

```

Then make it so that

1. Adding `_: Type` to some enum/structure requires `Type: UnnameableField`.

2. Any time enum/structure is constructed all unnameable fields appear during construction with value `VALUE`. I.e. the following is equivalent:

3. It is undefined behaviour to have a structure with unnameable field and have anything other than `VALUE` there. (This is to create niches.)

This way `UnnameableField` will solve three problems: people sometimes needing padding fields which just add noise to the code, people wanting to use space wasted as padding for niches and your problem of not wanting to write phantom fields.

This also does not introduce inconsistencies with the rest of the language: `_: T` meaning `_: PhantomData<T>` is really unexpected.

(If you feel this is a good idea deserving of RFC you are free to write one, I do not feel strongly enough about either padding or having to write PhantomData, so probably will not write it myself. There is also some more thinking needed: I am not sure about how easy this would be for common unsafe use-cases as you cannot take a chunk of initialized memory and put data for unnameable fields there without help of some compiler magic.)

[Next page](https://internals.rust-lang.org/t/phantomdata-without-field-syntax-noise/21062.md?page=2)
