Conversation
4664ee0 to
16018f0
Compare
| pub unsafe trait Trigger<E: Event> { | ||
| /// For any `'long: 'short`, `State<'long>` must have the same layout as | ||
| /// `State<'short>` and must be valid to reinterpret as `State<'short>` for | ||
| /// the duration of the shorter borrow. |
There was a problem hiding this comment.
I can't review the PR right now, but I want to note that reinterpreting State<'long> as State<'short> also means that it cannot expose a way to get &mut Something<'short> (which includes public fields of type Something<'short>
I cannot check what this PR does, but that was the original issue and reason why On/Trigger had this lifetime hack.
(Also sorry for commenting only on this line, Github on mobile is terrible)
There was a problem hiding this comment.
I can't review the PR right now, but I want to note that reinterpreting
State<'long>asState<'short>also means that it cannot expose a way to get&mut Something<'short>(which includes public fields of typeSomething<'short>I cannot check what this PR does, but that was the original issue and reason why
On/Triggerhad this lifetime hack.
I think the fn reborrow() method having the extra lifetime parameter in Self::State<'_> is what addresses this issue. Reviewing your comments at #20731 (comment), you said:
Here however we're casting it to a
&mut E::Trigger<'w>for some arbitrary lifetime'w. This is fine only as long as nothing about the lifetime'wthat 's different from'ais ever exposed to safe code, effectively forcing the safe code to be universally quantifed over a set of lifetimes that must include the original'a.
I think that extra lifetime parameter gives us that universal quantification. The clever part is that the parameter is on a trait method on Trigger now, so it doesn't leak into On!
There was a problem hiding this comment.
I've been looking at the PR a bit more and I think it should be good (don't quote me on that yet!) but I think the motivation is not quite right.
I think what makes it safe is that On has only one lifetime thanks to reborrowing, and the trigger ref also has only one lifetime thanks to reborrowing.
I don't see a reason for requiring the State to be transmutable though. The reborrowing is done by reborrow, there seems to be no transmute going on.
| // - The implementation abides by the other safety constraints defined in [`Trigger`] | ||
| unsafe impl<E: AnimationEvent + for<'a> Event<Trigger<'a> = AnimationEventTrigger>> Trigger<E> | ||
| // SAFETY: `AnimationEventTrigger` has no lifetimes. | ||
| unsafe impl<E: AnimationEvent + Event<Trigger = AnimationEventTrigger>> Trigger<E> |
There was a problem hiding this comment.
While this does simplify the lifetimes in the top level API, it also complicates the "type story" for triggers generally. I don't fully understand the "trigger target queries" stuff. Do you have a proposal link and/or a practical example of the API you're aiming for?
| /// | ||
| /// This trait can be derived. | ||
| pub trait AnimationEvent: Clone + for<'a> Event<Trigger<'a> = AnimationEventTrigger> {} | ||
| pub trait AnimationEvent: Clone + Event<Trigger = AnimationEventTrigger> {} |
There was a problem hiding this comment.
I like all the places where we got to get rid of for<'a>!
| pub unsafe trait Trigger<E: Event> { | ||
| /// For any `'long: 'short`, `State<'long>` must have the same layout as | ||
| /// `State<'short>` and must be valid to reinterpret as `State<'short>` for | ||
| /// the duration of the shorter borrow. |
There was a problem hiding this comment.
I can't review the PR right now, but I want to note that reinterpreting
State<'long>asState<'short>also means that it cannot expose a way to get&mut Something<'short>(which includes public fields of typeSomething<'short>I cannot check what this PR does, but that was the original issue and reason why
On/Triggerhad this lifetime hack.
I think the fn reborrow() method having the extra lifetime parameter in Self::State<'_> is what addresses this issue. Reviewing your comments at #20731 (comment), you said:
Here however we're casting it to a
&mut E::Trigger<'w>for some arbitrary lifetime'w. This is fine only as long as nothing about the lifetime'wthat 's different from'ais ever exposed to safe code, effectively forcing the safe code to be universally quantifed over a set of lifetimes that must include the original'a.
I think that extra lifetime parameter gives us that universal quantification. The clever part is that the parameter is on a trait method on Trigger now, so it doesn't leak into On!
|
Updated based on feedback; I think it's much clearer now. |
chescock
left a comment
There was a problem hiding this comment.
Yeah, this looks nice and clear!
There is a lot of type complexity, but I think the complexity is fundamental to allowing both mutable state in PropagateEntityTrigger::propagate and borrowed data in EntityComponentsTrigger, and this hides it away from the user a bit better than all the HRTBs, while also needing less unsafe.
| type State<'input> = PropagateEntityTrigger<AUTO_PROPAGATE, E, T>; | ||
| type View<'input> = &'input mut PropagateEntityTrigger<AUTO_PROPAGATE, E, T>; |
There was a problem hiding this comment.
Nit, but it might be more clear to use the Self alias for these associated types where possible (so, every impl but EntityComponentsTrigger).
| type State<'input> = PropagateEntityTrigger<AUTO_PROPAGATE, E, T>; | |
| type View<'input> = &'input mut PropagateEntityTrigger<AUTO_PROPAGATE, E, T>; | |
| type State<'input> = Self; | |
| type View<'input> = &'input mut Self; |
Trashtalk217
left a comment
There was a problem hiding this comment.
It all looks good and it's a nice little bit of cleanup of the lifetime soup.
Objective
Simplify the safety story of
OnandTrigger, and reduce the number of lifetimes onOnfrom two to one.This is a cleanup pass in preparation to support observer target queries,
On<'world, 'state, 'input, E: EventPattern, D: QueryData = (), F: QueryFilter = ()>.Solution
Add and require support for
Triggerreborrowing:Event::TriggerE: Event<Trigger = Self>requirement directly toTrigger's trait, removing theunsaferequirement for it.Trigger::State<'a>, which is the type that's passed toWorld::triggerandCommands::triggerTrigger::View<'a>, which is the type passed into observer systems as a mutable view of theTrigger::StateStateforPropagateEntityTrigger, in order to provide access to apropagate: &mut boolTrigger::reborrowto convert from a&'a mut Trigger::State<'_>to aTrigger::View<'a>(owned)EventTriggerStateandEventTriggerViewtype aliases for ease of access (replacingEventPatternTrigger)Testing
No new tests were added, suggestions welcome.