Skip to content

Remove Scene/SceneList impls for unit (), replace with unit struct#24033

Closed
laundmo wants to merge 1 commit into
bevyengine:mainfrom
laundmo:bevy-scene-no-unit-impls
Closed

Remove Scene/SceneList impls for unit (), replace with unit struct#24033
laundmo wants to merge 1 commit into
bevyengine:mainfrom
laundmo:bevy-scene-no-unit-impls

Conversation

@laundmo
Copy link
Copy Markdown
Contributor

@laundmo laundmo commented Apr 29, 2026

Objective

Having Scene and SceneList implemented for () (unit) can be a source of confusion if users forget a ;. See this already happening before bsn is released here: https://discord.com/channels/691052431525675048/691052431974465548/1498960812662849637
The intention of this is to cause an error when ; is accidentally used at the end of a expression which evaluates to impl Scene/impl SceneList

Solution

The issue was that () is both the value any expression ending in ; evaluates to, and the value for empty the corresponding traits were implemented for by all_tuples!

By raising the start of all_tuples! to 1 instead of 0, and implementing SceneList for a new unit struct, the implementation for () can be avoided. Luckily, everywhere where an empty Scene/SceneList was used in at least Bevy itself, it was done through bsn_list!() which ultimately created the () by calling out to bevy_scene::macro_utils::auto_nest_tuple!, allowing me to replace the () there with the new unit struct.

This means, empty BSN scenes and scene lists now consist of the new EmptyTuple unit struct instead of (), and an accidental ; now raises "error[E0277]: the trait bound (): bevy_scene::Scene is not satisfied"

Testing

  • cargo test
  • bevy_city example with a bsn_list!() changed to empty
  • feathers_gallery example with a bsn_list!() changed to empty
  • test with crates already using main?

@alice-i-cecile alice-i-cecile requested a review from cart April 29, 2026 19:02
@alice-i-cecile alice-i-cecile added this to the 0.19 milestone Apr 29, 2026
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Apr 29, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in ECS Apr 29, 2026
@alice-i-cecile alice-i-cecile added A-Scenes Composing and serializing ECS objects X-Contentious There are nontrivial implications that should be thought through S-Needs-Review Needs reviewer attention (from anyone!) to move forward and removed A-ECS Entities, components, systems, and events labels Apr 29, 2026
/// to add IDE support for nested type names, as it allows us to pass the input Ident from the input to the output code.
pub const fn touch_type<T>() {}

#[doc(hidden)]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments here about why this exists please. This is going to be very mysterious in the code in 5 years!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair, will do

}

#[test]
fn empty_scene_expressions() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nice to add a compile failure test for this + the ; case, but I won't block on it.

@alice-i-cecile
Copy link
Copy Markdown
Member

On board with this idea: that's a major footgun that we should avoid.

@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Apr 29, 2026

#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct EmptyTuple;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't particularly like this pattern. () is semantically an empty tuple and Scene is implemented for tuples.

Having () represent an empty Scene is also useful:

  • You can assign it in places like type Scene = ();. Because Scene is implemented for tuples, this is natural.
  • It lets you do fn scene() -> impl Scene {} as a form of "todo". Given that fn scene() -> impl Scene{ todo!() } doesn't work anymore (thanks to some semi-recent rust changes), I quite like having this as an option.

A custom EmptyTuple type also just introduces a form of "jank" to the API that makes everything just a little bit harder to understand (as a reader of docs, an author of internals, and a consumer of public APIs).

Yes, bsn! papers over all of this for most people. But Scene is intended to be an API that is usable on its own.

Copy link
Copy Markdown
Member

@cart cart Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that you can do this:

fn scene() -> impl Scene {
  ()
}

And then expand into this:

fn scene() -> impl Scene {
  (
    Sprite::patch(|value, context| { value.image = "sprite.png".into() }),
    Transform::patch(|value, context| { }),
  )
}

Copy link
Copy Markdown
Contributor Author

@laundmo laundmo Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So your solution to the issue of accidental ; is to keep it, or are you proposing that theres some other solution besides something like this?

i should have mentioned: the motivation for this was https://discord.com/channels/691052431525675048/691052431974465548/1498960812662849637

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There really is a core tension in Rust that () stands both for "empty tuple" and "no return"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah my argument is "yeah this is annoying, but it is a behavior inherent to Rust semantics". This is a problem everywhere the "trait implememented for tuples" pattern is used:

fn my_bundle() -> impl Bundle {
  (Transform::default(), Name::new("hello"));
}

It is also true for any trait that () has an impl for:

fn some_default() -> impl Default {
    vec![10usize];
}

@laundmo laundmo closed this Apr 29, 2026
@github-project-automation github-project-automation Bot moved this from Needs SME Triage to Done in ECS Apr 29, 2026
yonas pushed a commit to yonasBSD/bevy that referenced this pull request Apr 30, 2026
…vyengine#24040)

# Objective

See for more details
- bevyengine#24033

tldr: prevent accidental `;` in `-> impl SceneList`/`-> impl SceneList`
functions from just being accepted

## Solution

add `#[must_use]` to `SceneScope` and `SceneListScope` as recommended by
@chescock

## Testing

- [x] cargo test
- [x] adding semicolon to a feathers `-> impl Scene` function to see the
must use error

idk, its an attribute, theres not much to test
tychedelia pushed a commit to processing/bevy that referenced this pull request Apr 30, 2026
…vyengine#24040)

# Objective

See for more details
- bevyengine#24033

tldr: prevent accidental `;` in `-> impl SceneList`/`-> impl SceneList`
functions from just being accepted

## Solution

add `#[must_use]` to `SceneScope` and `SceneListScope` as recommended by
@chescock

## Testing

- [x] cargo test
- [x] adding semicolon to a feathers `-> impl Scene` function to see the
must use error

idk, its an attribute, theres not much to test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Scenes Composing and serializing ECS objects C-Usability A targeted quality-of-life change that makes Bevy easier to use S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged X-Contentious There are nontrivial implications that should be thought through

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants