bevy_ecs/change_detection.rs
1//! Types that detect when their internal data mutate.
2
3use crate::{
4 component::{Tick, TickCells},
5 ptr::PtrMut,
6 system::Resource,
7};
8use bevy_ptr::{Ptr, UnsafeCellDeref};
9use std::mem;
10use std::ops::{Deref, DerefMut};
11
12/// The (arbitrarily chosen) minimum number of world tick increments between `check_tick` scans.
13///
14/// Change ticks can only be scanned when systems aren't running. Thus, if the threshold is `N`,
15/// the maximum is `2 * N - 1` (i.e. the world ticks `N - 1` times, then `N` times).
16///
17/// If no change is older than `u32::MAX - (2 * N - 1)` following a scan, none of their ages can
18/// overflow and cause false positives.
19// (518,400,000 = 1000 ticks per frame * 144 frames per second * 3600 seconds per hour)
20pub const CHECK_TICK_THRESHOLD: u32 = 518_400_000;
21
22/// The maximum change tick difference that won't overflow before the next `check_tick` scan.
23///
24/// Changes stop being detected once they become this old.
25pub const MAX_CHANGE_AGE: u32 = u32::MAX - (2 * CHECK_TICK_THRESHOLD - 1);
26
27/// Types that can read change detection information.
28/// This change detection is controlled by [`DetectChangesMut`] types such as [`ResMut`].
29///
30/// ## Example
31/// Using types that implement [`DetectChanges`], such as [`Res`], provide
32/// a way to query if a value has been mutated in another system.
33///
34/// ```
35/// use bevy_ecs::prelude::*;
36///
37/// #[derive(Resource)]
38/// struct MyResource(u32);
39///
40/// fn my_system(mut resource: Res<MyResource>) {
41/// if resource.is_changed() {
42/// println!("My component was mutated!");
43/// }
44/// }
45/// ```
46pub trait DetectChanges {
47 /// Returns `true` if this value was added after the system last ran.
48 fn is_added(&self) -> bool;
49
50 /// Returns `true` if this value was added or mutably dereferenced
51 /// either since the last time the system ran or, if the system never ran,
52 /// since the beginning of the program.
53 ///
54 /// To check if the value was mutably dereferenced only,
55 /// use `this.is_changed() && !this.is_added()`.
56 fn is_changed(&self) -> bool;
57
58 /// Returns the change tick recording the time this data was most recently changed.
59 ///
60 /// Note that components and resources are also marked as changed upon insertion.
61 ///
62 /// For comparison, the previous change tick of a system can be read using the
63 /// [`SystemChangeTick`](crate::system::SystemChangeTick)
64 /// [`SystemParam`](crate::system::SystemParam).
65 fn last_changed(&self) -> Tick;
66}
67
68/// Types that implement reliable change detection.
69///
70/// ## Example
71/// Using types that implement [`DetectChangesMut`], such as [`ResMut`], provide
72/// a way to query if a value has been mutated in another system.
73/// Normally change detection is triggered by either [`DerefMut`] or [`AsMut`], however
74/// it can be manually triggered via [`set_changed`](DetectChangesMut::set_changed).
75///
76/// To ensure that changes are only triggered when the value actually differs,
77/// check if the value would change before assignment, such as by checking that `new != old`.
78/// You must be *sure* that you are not mutably dereferencing in this process.
79///
80/// [`set_if_neq`](DetectChangesMut::set_if_neq) is a helper
81/// method for this common functionality.
82///
83/// ```
84/// use bevy_ecs::prelude::*;
85///
86/// #[derive(Resource)]
87/// struct MyResource(u32);
88///
89/// fn my_system(mut resource: ResMut<MyResource>) {
90/// if resource.is_changed() {
91/// println!("My resource was mutated!");
92/// }
93///
94/// resource.0 = 42; // triggers change detection via [`DerefMut`]
95/// }
96/// ```
97///
98pub trait DetectChangesMut: DetectChanges {
99 /// The type contained within this smart pointer
100 ///
101 /// For example, for `ResMut<T>` this would be `T`.
102 type Inner: ?Sized;
103
104 /// Flags this value as having been changed.
105 ///
106 /// Mutably accessing this smart pointer will automatically flag this value as having been changed.
107 /// However, mutation through interior mutability requires manual reporting.
108 ///
109 /// **Note**: This operation cannot be undone.
110 fn set_changed(&mut self);
111
112 /// Manually sets the change tick recording the time when this data was last mutated.
113 ///
114 /// # Warning
115 /// This is a complex and error-prone operation, primarily intended for use with rollback networking strategies.
116 /// If you merely want to flag this data as changed, use [`set_changed`](DetectChangesMut::set_changed) instead.
117 /// If you want to avoid triggering change detection, use [`bypass_change_detection`](DetectChangesMut::bypass_change_detection) instead.
118 fn set_last_changed(&mut self, last_changed: Tick);
119
120 /// Manually bypasses change detection, allowing you to mutate the underlying value without updating the change tick.
121 ///
122 /// # Warning
123 /// This is a risky operation, that can have unexpected consequences on any system relying on this code.
124 /// However, it can be an essential escape hatch when, for example,
125 /// you are trying to synchronize representations using change detection and need to avoid infinite recursion.
126 fn bypass_change_detection(&mut self) -> &mut Self::Inner;
127
128 /// Overwrites this smart pointer with the given value, if and only if `*self != value`.
129 /// Returns `true` if the value was overwritten, and returns `false` if it was not.
130 ///
131 /// This is useful to ensure change detection is only triggered when the underlying value
132 /// changes, instead of every time it is mutably accessed.
133 ///
134 /// If you're dealing with non-trivial structs which have multiple fields of non-trivial size,
135 /// then consider applying a `map_unchanged` beforehand to allow changing only the relevant
136 /// field and prevent unnecessary copying and cloning.
137 /// See the docs of [`Mut::map_unchanged`], [`MutUntyped::map_unchanged`],
138 /// [`ResMut::map_unchanged`] or [`NonSendMut::map_unchanged`] for an example
139 ///
140 /// If you need the previous value, use [`replace_if_neq`](DetectChangesMut::replace_if_neq).
141 ///
142 /// # Examples
143 ///
144 /// ```
145 /// # use bevy_ecs::{prelude::*, schedule::common_conditions::resource_changed};
146 /// #[derive(Resource, PartialEq, Eq)]
147 /// pub struct Score(u32);
148 ///
149 /// fn reset_score(mut score: ResMut<Score>) {
150 /// // Set the score to zero, unless it is already zero.
151 /// score.set_if_neq(Score(0));
152 /// }
153 /// # let mut world = World::new();
154 /// # world.insert_resource(Score(1));
155 /// # let mut score_changed = IntoSystem::into_system(resource_changed::<Score>);
156 /// # score_changed.initialize(&mut world);
157 /// # score_changed.run((), &mut world);
158 /// #
159 /// # let mut schedule = Schedule::default();
160 /// # schedule.add_systems(reset_score);
161 /// #
162 /// # // first time `reset_score` runs, the score is changed.
163 /// # schedule.run(&mut world);
164 /// # assert!(score_changed.run((), &mut world));
165 /// # // second time `reset_score` runs, the score is not changed.
166 /// # schedule.run(&mut world);
167 /// # assert!(!score_changed.run((), &mut world));
168 /// ```
169 #[inline]
170 fn set_if_neq(&mut self, value: Self::Inner) -> bool
171 where
172 Self::Inner: Sized + PartialEq,
173 {
174 let old = self.bypass_change_detection();
175 if *old != value {
176 *old = value;
177 self.set_changed();
178 true
179 } else {
180 false
181 }
182 }
183
184 /// Overwrites this smart pointer with the given value, if and only if `*self != value`,
185 /// returning the previous value if this occurs.
186 ///
187 /// This is useful to ensure change detection is only triggered when the underlying value
188 /// changes, instead of every time it is mutably accessed.
189 ///
190 /// If you're dealing with non-trivial structs which have multiple fields of non-trivial size,
191 /// then consider applying a [`map_unchanged`](Mut::map_unchanged) beforehand to allow
192 /// changing only the relevant field and prevent unnecessary copying and cloning.
193 /// See the docs of [`Mut::map_unchanged`], [`MutUntyped::map_unchanged`],
194 /// [`ResMut::map_unchanged`] or [`NonSendMut::map_unchanged`] for an example
195 ///
196 /// If you don't need the previous value, use [`set_if_neq`](DetectChangesMut::set_if_neq).
197 ///
198 /// # Examples
199 ///
200 /// ```
201 /// # use bevy_ecs::{prelude::*, schedule::common_conditions::{resource_changed, on_event}};
202 /// #[derive(Resource, PartialEq, Eq)]
203 /// pub struct Score(u32);
204 ///
205 /// #[derive(Event, PartialEq, Eq)]
206 /// pub struct ScoreChanged {
207 /// current: u32,
208 /// previous: u32,
209 /// }
210 ///
211 /// fn reset_score(mut score: ResMut<Score>, mut score_changed: EventWriter<ScoreChanged>) {
212 /// // Set the score to zero, unless it is already zero.
213 /// let new_score = 0;
214 /// if let Some(Score(previous_score)) = score.replace_if_neq(Score(new_score)) {
215 /// // If `score` change, emit a `ScoreChanged` event.
216 /// score_changed.send(ScoreChanged {
217 /// current: new_score,
218 /// previous: previous_score,
219 /// });
220 /// }
221 /// }
222 /// # let mut world = World::new();
223 /// # world.insert_resource(Events::<ScoreChanged>::default());
224 /// # world.insert_resource(Score(1));
225 /// # let mut score_changed = IntoSystem::into_system(resource_changed::<Score>);
226 /// # score_changed.initialize(&mut world);
227 /// # score_changed.run((), &mut world);
228 /// #
229 /// # let mut score_changed_event = IntoSystem::into_system(on_event::<ScoreChanged>());
230 /// # score_changed_event.initialize(&mut world);
231 /// # score_changed_event.run((), &mut world);
232 /// #
233 /// # let mut schedule = Schedule::default();
234 /// # schedule.add_systems(reset_score);
235 /// #
236 /// # // first time `reset_score` runs, the score is changed.
237 /// # schedule.run(&mut world);
238 /// # assert!(score_changed.run((), &mut world));
239 /// # assert!(score_changed_event.run((), &mut world));
240 /// # // second time `reset_score` runs, the score is not changed.
241 /// # schedule.run(&mut world);
242 /// # assert!(!score_changed.run((), &mut world));
243 /// # assert!(!score_changed_event.run((), &mut world));
244 /// ```
245 #[inline]
246 #[must_use = "If you don't need to handle the previous value, use `set_if_neq` instead."]
247 fn replace_if_neq(&mut self, value: Self::Inner) -> Option<Self::Inner>
248 where
249 Self::Inner: Sized + PartialEq,
250 {
251 let old = self.bypass_change_detection();
252 if *old != value {
253 let previous = mem::replace(old, value);
254 self.set_changed();
255 Some(previous)
256 } else {
257 None
258 }
259 }
260}
261
262macro_rules! change_detection_impl {
263 ($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
264 impl<$($generics),* : ?Sized $(+ $traits)?> DetectChanges for $name<$($generics),*> {
265 #[inline]
266 fn is_added(&self) -> bool {
267 self.ticks
268 .added
269 .is_newer_than(self.ticks.last_run, self.ticks.this_run)
270 }
271
272 #[inline]
273 fn is_changed(&self) -> bool {
274 self.ticks
275 .changed
276 .is_newer_than(self.ticks.last_run, self.ticks.this_run)
277 }
278
279 #[inline]
280 fn last_changed(&self) -> Tick {
281 *self.ticks.changed
282 }
283 }
284
285 impl<$($generics),*: ?Sized $(+ $traits)?> Deref for $name<$($generics),*> {
286 type Target = $target;
287
288 #[inline]
289 fn deref(&self) -> &Self::Target {
290 self.value
291 }
292 }
293
294 impl<$($generics),* $(: $traits)?> AsRef<$target> for $name<$($generics),*> {
295 #[inline]
296 fn as_ref(&self) -> &$target {
297 self.deref()
298 }
299 }
300 }
301}
302
303macro_rules! change_detection_mut_impl {
304 ($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
305 impl<$($generics),* : ?Sized $(+ $traits)?> DetectChangesMut for $name<$($generics),*> {
306 type Inner = $target;
307
308 #[inline]
309 fn set_changed(&mut self) {
310 *self.ticks.changed = self.ticks.this_run;
311 }
312
313 #[inline]
314 fn set_last_changed(&mut self, last_changed: Tick) {
315 *self.ticks.changed = last_changed;
316 }
317
318 #[inline]
319 fn bypass_change_detection(&mut self) -> &mut Self::Inner {
320 self.value
321 }
322 }
323
324 impl<$($generics),* : ?Sized $(+ $traits)?> DerefMut for $name<$($generics),*> {
325 #[inline]
326 fn deref_mut(&mut self) -> &mut Self::Target {
327 self.set_changed();
328 self.value
329 }
330 }
331
332 impl<$($generics),* $(: $traits)?> AsMut<$target> for $name<$($generics),*> {
333 #[inline]
334 fn as_mut(&mut self) -> &mut $target {
335 self.deref_mut()
336 }
337 }
338 };
339}
340
341macro_rules! impl_methods {
342 ($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
343 impl<$($generics),* : ?Sized $(+ $traits)?> $name<$($generics),*> {
344 /// Consume `self` and return a mutable reference to the
345 /// contained value while marking `self` as "changed".
346 #[inline]
347 pub fn into_inner(mut self) -> &'w mut $target {
348 self.set_changed();
349 self.value
350 }
351
352 /// Returns a `Mut<>` with a smaller lifetime.
353 /// This is useful if you have `&mut
354 #[doc = stringify!($name)]
355 /// <T>`, but you need a `Mut<T>`.
356 pub fn reborrow(&mut self) -> Mut<'_, $target> {
357 Mut {
358 value: self.value,
359 ticks: TicksMut {
360 added: self.ticks.added,
361 changed: self.ticks.changed,
362 last_run: self.ticks.last_run,
363 this_run: self.ticks.this_run,
364 }
365 }
366 }
367
368 /// Maps to an inner value by applying a function to the contained reference, without flagging a change.
369 ///
370 /// You should never modify the argument passed to the closure -- if you want to modify the data
371 /// without flagging a change, consider using [`DetectChangesMut::bypass_change_detection`] to make your intent explicit.
372 ///
373 /// ```
374 /// # use bevy_ecs::prelude::*;
375 /// # #[derive(PartialEq)] pub struct Vec2;
376 /// # impl Vec2 { pub const ZERO: Self = Self; }
377 /// # #[derive(Component)] pub struct Transform { translation: Vec2 }
378 /// // When run, zeroes the translation of every entity.
379 /// fn reset_positions(mut transforms: Query<&mut Transform>) {
380 /// for transform in &mut transforms {
381 /// // We pinky promise not to modify `t` within the closure.
382 /// // Breaking this promise will result in logic errors, but will never cause undefined behavior.
383 /// let mut translation = transform.map_unchanged(|t| &mut t.translation);
384 /// // Only reset the translation if it isn't already zero;
385 /// translation.set_if_neq(Vec2::ZERO);
386 /// }
387 /// }
388 /// # bevy_ecs::system::assert_is_system(reset_positions);
389 /// ```
390 pub fn map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> &mut U) -> Mut<'w, U> {
391 Mut {
392 value: f(self.value),
393 ticks: self.ticks,
394 }
395 }
396
397 /// Allows you access to the dereferenced value of this pointer without immediately
398 /// triggering change detection.
399 pub fn as_deref_mut(&mut self) -> Mut<'_, <$target as Deref>::Target>
400 where $target: DerefMut
401 {
402 self.reborrow().map_unchanged(|v| v.deref_mut())
403 }
404
405 }
406 };
407}
408
409macro_rules! impl_debug {
410 ($name:ident < $( $generics:tt ),+ >, $($traits:ident)?) => {
411 impl<$($generics),* : ?Sized $(+ $traits)?> std::fmt::Debug for $name<$($generics),*>
412 where T: std::fmt::Debug
413 {
414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415 f.debug_tuple(stringify!($name))
416 .field(&self.value)
417 .finish()
418 }
419 }
420
421 };
422}
423
424#[derive(Clone)]
425pub(crate) struct Ticks<'w> {
426 pub(crate) added: &'w Tick,
427 pub(crate) changed: &'w Tick,
428 pub(crate) last_run: Tick,
429 pub(crate) this_run: Tick,
430}
431
432impl<'w> Ticks<'w> {
433 /// # Safety
434 /// This should never alias the underlying ticks with a mutable one such as `TicksMut`.
435 #[inline]
436 pub(crate) unsafe fn from_tick_cells(
437 cells: TickCells<'w>,
438 last_run: Tick,
439 this_run: Tick,
440 ) -> Self {
441 Self {
442 // SAFETY: Caller ensures there is no mutable access to the cell.
443 added: unsafe { cells.added.deref() },
444 // SAFETY: Caller ensures there is no mutable access to the cell.
445 changed: unsafe { cells.changed.deref() },
446 last_run,
447 this_run,
448 }
449 }
450}
451
452pub(crate) struct TicksMut<'w> {
453 pub(crate) added: &'w mut Tick,
454 pub(crate) changed: &'w mut Tick,
455 pub(crate) last_run: Tick,
456 pub(crate) this_run: Tick,
457}
458
459impl<'w> TicksMut<'w> {
460 /// # Safety
461 /// This should never alias the underlying ticks. All access must be unique.
462 #[inline]
463 pub(crate) unsafe fn from_tick_cells(
464 cells: TickCells<'w>,
465 last_run: Tick,
466 this_run: Tick,
467 ) -> Self {
468 Self {
469 // SAFETY: Caller ensures there is no alias to the cell.
470 added: unsafe { cells.added.deref_mut() },
471 // SAFETY: Caller ensures there is no alias to the cell.
472 changed: unsafe { cells.changed.deref_mut() },
473 last_run,
474 this_run,
475 }
476 }
477}
478
479impl<'w> From<TicksMut<'w>> for Ticks<'w> {
480 fn from(ticks: TicksMut<'w>) -> Self {
481 Ticks {
482 added: ticks.added,
483 changed: ticks.changed,
484 last_run: ticks.last_run,
485 this_run: ticks.this_run,
486 }
487 }
488}
489
490/// Shared borrow of a [`Resource`].
491///
492/// See the [`Resource`] documentation for usage.
493///
494/// If you need a unique mutable borrow, use [`ResMut`] instead.
495///
496/// # Panics
497///
498/// Panics when used as a [`SystemParameter`](crate::system::SystemParam) if the resource does not exist.
499///
500/// Use `Option<Res<T>>` instead if the resource might not always exist.
501pub struct Res<'w, T: ?Sized + Resource> {
502 pub(crate) value: &'w T,
503 pub(crate) ticks: Ticks<'w>,
504}
505
506impl<'w, T: Resource> Res<'w, T> {
507 /// Copies a reference to a resource.
508 ///
509 /// Note that unless you actually need an instance of `Res<T>`, you should
510 /// prefer to just convert it to `&T` which can be freely copied.
511 #[allow(clippy::should_implement_trait)]
512 pub fn clone(this: &Self) -> Self {
513 Self {
514 value: this.value,
515 ticks: this.ticks.clone(),
516 }
517 }
518
519 /// Due to lifetime limitations of the `Deref` trait, this method can be used to obtain a
520 /// reference of the [`Resource`] with a lifetime bound to `'w` instead of the lifetime of the
521 /// struct itself.
522 pub fn into_inner(self) -> &'w T {
523 self.value
524 }
525}
526
527impl<'w, T: Resource> From<ResMut<'w, T>> for Res<'w, T> {
528 fn from(res: ResMut<'w, T>) -> Self {
529 Self {
530 value: res.value,
531 ticks: res.ticks.into(),
532 }
533 }
534}
535
536impl<'w, 'a, T: Resource> IntoIterator for &'a Res<'w, T>
537where
538 &'a T: IntoIterator,
539{
540 type Item = <&'a T as IntoIterator>::Item;
541 type IntoIter = <&'a T as IntoIterator>::IntoIter;
542
543 fn into_iter(self) -> Self::IntoIter {
544 self.value.into_iter()
545 }
546}
547change_detection_impl!(Res<'w, T>, T, Resource);
548impl_debug!(Res<'w, T>, Resource);
549
550/// Unique mutable borrow of a [`Resource`].
551///
552/// See the [`Resource`] documentation for usage.
553///
554/// If you need a shared borrow, use [`Res`] instead.
555///
556/// # Panics
557///
558/// Panics when used as a [`SystemParam`](crate::system::SystemParam) if the resource does not exist.
559///
560/// Use `Option<ResMut<T>>` instead if the resource might not always exist.
561pub struct ResMut<'w, T: ?Sized + Resource> {
562 pub(crate) value: &'w mut T,
563 pub(crate) ticks: TicksMut<'w>,
564}
565
566impl<'w, 'a, T: Resource> IntoIterator for &'a ResMut<'w, T>
567where
568 &'a T: IntoIterator,
569{
570 type Item = <&'a T as IntoIterator>::Item;
571 type IntoIter = <&'a T as IntoIterator>::IntoIter;
572
573 fn into_iter(self) -> Self::IntoIter {
574 self.value.into_iter()
575 }
576}
577
578impl<'w, 'a, T: Resource> IntoIterator for &'a mut ResMut<'w, T>
579where
580 &'a mut T: IntoIterator,
581{
582 type Item = <&'a mut T as IntoIterator>::Item;
583 type IntoIter = <&'a mut T as IntoIterator>::IntoIter;
584
585 fn into_iter(self) -> Self::IntoIter {
586 self.set_changed();
587 self.value.into_iter()
588 }
589}
590
591change_detection_impl!(ResMut<'w, T>, T, Resource);
592change_detection_mut_impl!(ResMut<'w, T>, T, Resource);
593impl_methods!(ResMut<'w, T>, T, Resource);
594impl_debug!(ResMut<'w, T>, Resource);
595
596impl<'w, T: Resource> From<ResMut<'w, T>> for Mut<'w, T> {
597 /// Convert this `ResMut` into a `Mut`. This allows keeping the change-detection feature of `Mut`
598 /// while losing the specificity of `ResMut` for resources.
599 fn from(other: ResMut<'w, T>) -> Mut<'w, T> {
600 Mut {
601 value: other.value,
602 ticks: other.ticks,
603 }
604 }
605}
606
607/// Unique borrow of a non-[`Send`] resource.
608///
609/// Only [`Send`] resources may be accessed with the [`ResMut`] [`SystemParam`](crate::system::SystemParam). In case that the
610/// resource does not implement `Send`, this `SystemParam` wrapper can be used. This will instruct
611/// the scheduler to instead run the system on the main thread so that it doesn't send the resource
612/// over to another thread.
613///
614/// # Panics
615///
616/// Panics when used as a `SystemParameter` if the resource does not exist.
617///
618/// Use `Option<NonSendMut<T>>` instead if the resource might not always exist.
619pub struct NonSendMut<'w, T: ?Sized + 'static> {
620 pub(crate) value: &'w mut T,
621 pub(crate) ticks: TicksMut<'w>,
622}
623
624change_detection_impl!(NonSendMut<'w, T>, T,);
625change_detection_mut_impl!(NonSendMut<'w, T>, T,);
626impl_methods!(NonSendMut<'w, T>, T,);
627impl_debug!(NonSendMut<'w, T>,);
628
629impl<'w, T: 'static> From<NonSendMut<'w, T>> for Mut<'w, T> {
630 /// Convert this `NonSendMut` into a `Mut`. This allows keeping the change-detection feature of `Mut`
631 /// while losing the specificity of `NonSendMut`.
632 fn from(other: NonSendMut<'w, T>) -> Mut<'w, T> {
633 Mut {
634 value: other.value,
635 ticks: other.ticks,
636 }
637 }
638}
639
640/// Shared borrow of an entity's component with access to change detection.
641/// Similar to [`Mut`] but is immutable and so doesn't require unique access.
642///
643/// # Examples
644///
645/// These two systems produce the same output.
646///
647/// ```
648/// # use bevy_ecs::change_detection::DetectChanges;
649/// # use bevy_ecs::query::{Changed, With};
650/// # use bevy_ecs::system::Query;
651/// # use bevy_ecs::world::Ref;
652/// # use bevy_ecs_macros::Component;
653/// # #[derive(Component)]
654/// # struct MyComponent;
655///
656/// fn how_many_changed_1(query: Query<(), Changed<MyComponent>>) {
657/// println!("{} changed", query.iter().count());
658/// }
659///
660/// fn how_many_changed_2(query: Query<Ref<MyComponent>>) {
661/// println!("{} changed", query.iter().filter(|c| c.is_changed()).count());
662/// }
663/// ```
664pub struct Ref<'w, T: ?Sized> {
665 pub(crate) value: &'w T,
666 pub(crate) ticks: Ticks<'w>,
667}
668
669impl<'w, T: ?Sized> Ref<'w, T> {
670 /// Returns the reference wrapped by this type. The reference is allowed to outlive `self`, which makes this method more flexible than simply borrowing `self`.
671 pub fn into_inner(self) -> &'w T {
672 self.value
673 }
674
675 /// Map `Ref` to a different type using `f`.
676 ///
677 /// This doesn't do anything else than call `f` on the wrapped value.
678 /// This is equivalent to [`Mut::map_unchanged`].
679 pub fn map<U: ?Sized>(self, f: impl FnOnce(&T) -> &U) -> Ref<'w, U> {
680 Ref {
681 value: f(self.value),
682 ticks: self.ticks,
683 }
684 }
685
686 /// Create a new `Ref` using provided values.
687 ///
688 /// This is an advanced feature, `Ref`s are designed to be _created_ by
689 /// engine-internal code and _consumed_ by end-user code.
690 ///
691 /// - `value` - The value wrapped by `Ref`.
692 /// - `added` - A [`Tick`] that stores the tick when the wrapped value was created.
693 /// - `changed` - A [`Tick`] that stores the last time the wrapped value was changed.
694 /// - `last_run` - A [`Tick`], occurring before `this_run`, which is used
695 /// as a reference to determine whether the wrapped value is newly added or changed.
696 /// - `this_run` - A [`Tick`] corresponding to the current point in time -- "now".
697 pub fn new(
698 value: &'w T,
699 added: &'w Tick,
700 changed: &'w Tick,
701 last_run: Tick,
702 this_run: Tick,
703 ) -> Ref<'w, T> {
704 Ref {
705 value,
706 ticks: Ticks {
707 added,
708 changed,
709 last_run,
710 this_run,
711 },
712 }
713 }
714}
715
716impl<'w, 'a, T> IntoIterator for &'a Ref<'w, T>
717where
718 &'a T: IntoIterator,
719{
720 type Item = <&'a T as IntoIterator>::Item;
721 type IntoIter = <&'a T as IntoIterator>::IntoIter;
722
723 fn into_iter(self) -> Self::IntoIter {
724 self.value.into_iter()
725 }
726}
727change_detection_impl!(Ref<'w, T>, T,);
728impl_debug!(Ref<'w, T>,);
729
730/// Unique mutable borrow of an entity's component or of a resource.
731///
732/// This can be used in queries to opt into change detection on both their mutable and immutable forms, as opposed to
733/// `&mut T`, which only provides access to change detection while in its mutable form:
734///
735/// ```rust
736/// # use bevy_ecs::prelude::*;
737/// # use bevy_ecs::query::QueryData;
738/// #
739/// #[derive(Component, Clone)]
740/// struct Name(String);
741///
742/// #[derive(Component, Clone, Copy)]
743/// struct Health(f32);
744///
745/// #[derive(Component, Clone, Copy)]
746/// struct Position {
747/// x: f32,
748/// y: f32,
749/// };
750///
751/// #[derive(Component, Clone, Copy)]
752/// struct Player {
753/// id: usize,
754/// };
755///
756/// #[derive(QueryData)]
757/// #[query_data(mutable)]
758/// struct PlayerQuery {
759/// id: &'static Player,
760///
761/// // Reacting to `PlayerName` changes is expensive, so we need to enable change detection when reading it.
762/// name: Mut<'static, Name>,
763///
764/// health: &'static mut Health,
765/// position: &'static mut Position,
766/// }
767///
768/// fn update_player_avatars(players_query: Query<PlayerQuery>) {
769/// // The item returned by the iterator is of type `PlayerQueryReadOnlyItem`.
770/// for player in players_query.iter() {
771/// if player.name.is_changed() {
772/// // Update the player's name. This clones a String, and so is more expensive.
773/// update_player_name(player.id, player.name.clone());
774/// }
775///
776/// // Update the health bar.
777/// update_player_health(player.id, *player.health);
778///
779/// // Update the player's position.
780/// update_player_position(player.id, *player.position);
781/// }
782/// }
783///
784/// # bevy_ecs::system::assert_is_system(update_player_avatars);
785///
786/// # fn update_player_name(player: &Player, new_name: Name) {}
787/// # fn update_player_health(player: &Player, new_health: Health) {}
788/// # fn update_player_position(player: &Player, new_position: Position) {}
789/// ```
790pub struct Mut<'w, T: ?Sized> {
791 pub(crate) value: &'w mut T,
792 pub(crate) ticks: TicksMut<'w>,
793}
794
795impl<'w, T: ?Sized> Mut<'w, T> {
796 /// Creates a new change-detection enabled smart pointer.
797 /// In almost all cases you do not need to call this method manually,
798 /// as instances of `Mut` will be created by engine-internal code.
799 ///
800 /// Many use-cases of this method would be better served by [`Mut::map_unchanged`]
801 /// or [`Mut::reborrow`].
802 ///
803 /// - `value` - The value wrapped by this smart pointer.
804 /// - `added` - A [`Tick`] that stores the tick when the wrapped value was created.
805 /// - `last_changed` - A [`Tick`] that stores the last time the wrapped value was changed.
806 /// This will be updated to the value of `change_tick` if the returned smart pointer
807 /// is modified.
808 /// - `last_run` - A [`Tick`], occurring before `this_run`, which is used
809 /// as a reference to determine whether the wrapped value is newly added or changed.
810 /// - `this_run` - A [`Tick`] corresponding to the current point in time -- "now".
811 pub fn new(
812 value: &'w mut T,
813 added: &'w mut Tick,
814 last_changed: &'w mut Tick,
815 last_run: Tick,
816 this_run: Tick,
817 ) -> Self {
818 Self {
819 value,
820 ticks: TicksMut {
821 added,
822 changed: last_changed,
823 last_run,
824 this_run,
825 },
826 }
827 }
828}
829
830impl<'w, T: ?Sized> From<Mut<'w, T>> for Ref<'w, T> {
831 fn from(mut_ref: Mut<'w, T>) -> Self {
832 Self {
833 value: mut_ref.value,
834 ticks: mut_ref.ticks.into(),
835 }
836 }
837}
838
839impl<'w, 'a, T> IntoIterator for &'a Mut<'w, T>
840where
841 &'a T: IntoIterator,
842{
843 type Item = <&'a T as IntoIterator>::Item;
844 type IntoIter = <&'a T as IntoIterator>::IntoIter;
845
846 fn into_iter(self) -> Self::IntoIter {
847 self.value.into_iter()
848 }
849}
850
851impl<'w, 'a, T> IntoIterator for &'a mut Mut<'w, T>
852where
853 &'a mut T: IntoIterator,
854{
855 type Item = <&'a mut T as IntoIterator>::Item;
856 type IntoIter = <&'a mut T as IntoIterator>::IntoIter;
857
858 fn into_iter(self) -> Self::IntoIter {
859 self.set_changed();
860 self.value.into_iter()
861 }
862}
863
864change_detection_impl!(Mut<'w, T>, T,);
865change_detection_mut_impl!(Mut<'w, T>, T,);
866impl_methods!(Mut<'w, T>, T,);
867impl_debug!(Mut<'w, T>,);
868
869/// Unique mutable borrow of resources or an entity's component.
870///
871/// Similar to [`Mut`], but not generic over the component type, instead
872/// exposing the raw pointer as a `*mut ()`.
873///
874/// Usually you don't need to use this and can instead use the APIs returning a
875/// [`Mut`], but in situations where the types are not known at compile time
876/// or are defined outside of rust this can be used.
877pub struct MutUntyped<'w> {
878 pub(crate) value: PtrMut<'w>,
879 pub(crate) ticks: TicksMut<'w>,
880}
881
882impl<'w> MutUntyped<'w> {
883 /// Returns the pointer to the value, marking it as changed.
884 ///
885 /// In order to avoid marking the value as changed, you need to call [`bypass_change_detection`](DetectChangesMut::bypass_change_detection).
886 #[inline]
887 pub fn into_inner(mut self) -> PtrMut<'w> {
888 self.set_changed();
889 self.value
890 }
891
892 /// Returns a [`MutUntyped`] with a smaller lifetime.
893 /// This is useful if you have `&mut MutUntyped`, but you need a `MutUntyped`.
894 #[inline]
895 pub fn reborrow(&mut self) -> MutUntyped {
896 MutUntyped {
897 value: self.value.reborrow(),
898 ticks: TicksMut {
899 added: self.ticks.added,
900 changed: self.ticks.changed,
901 last_run: self.ticks.last_run,
902 this_run: self.ticks.this_run,
903 },
904 }
905 }
906
907 /// Returns `true` if this value was changed or mutably dereferenced
908 /// either since a specific change tick.
909 pub fn has_changed_since(&self, tick: Tick) -> bool {
910 self.ticks.changed.is_newer_than(tick, self.ticks.this_run)
911 }
912
913 /// Returns a pointer to the value without taking ownership of this smart pointer, marking it as changed.
914 ///
915 /// In order to avoid marking the value as changed, you need to call [`bypass_change_detection`](DetectChangesMut::bypass_change_detection).
916 #[inline]
917 pub fn as_mut(&mut self) -> PtrMut<'_> {
918 self.set_changed();
919 self.value.reborrow()
920 }
921
922 /// Returns an immutable pointer to the value without taking ownership.
923 #[inline]
924 pub fn as_ref(&self) -> Ptr<'_> {
925 self.value.as_ref()
926 }
927
928 /// Turn this [`MutUntyped`] into a [`Mut`] by mapping the inner [`PtrMut`] to another value,
929 /// without flagging a change.
930 /// This function is the untyped equivalent of [`Mut::map_unchanged`].
931 ///
932 /// You should never modify the argument passed to the closure – if you want to modify the data without flagging a change, consider using [`bypass_change_detection`](DetectChangesMut::bypass_change_detection) to make your intent explicit.
933 ///
934 /// If you know the type of the value you can do
935 /// ```no_run
936 /// # use bevy_ecs::change_detection::{Mut, MutUntyped};
937 /// # let mut_untyped: MutUntyped = unimplemented!();
938 /// // SAFETY: ptr is of type `u8`
939 /// mut_untyped.map_unchanged(|ptr| unsafe { ptr.deref_mut::<u8>() });
940 /// ```
941 /// If you have a [`ReflectFromPtr`](bevy_reflect::ReflectFromPtr) that you know belongs to this [`MutUntyped`],
942 /// you can do
943 /// ```no_run
944 /// # use bevy_ecs::change_detection::{Mut, MutUntyped};
945 /// # let mut_untyped: MutUntyped = unimplemented!();
946 /// # let reflect_from_ptr: bevy_reflect::ReflectFromPtr = unimplemented!();
947 /// // SAFETY: from the context it is known that `ReflectFromPtr` was made for the type of the `MutUntyped`
948 /// mut_untyped.map_unchanged(|ptr| unsafe { reflect_from_ptr.as_reflect_mut(ptr) });
949 /// ```
950 pub fn map_unchanged<T: ?Sized>(self, f: impl FnOnce(PtrMut<'w>) -> &'w mut T) -> Mut<'w, T> {
951 Mut {
952 value: f(self.value),
953 ticks: self.ticks,
954 }
955 }
956
957 /// Transforms this [`MutUntyped`] into a [`Mut<T>`] with the same lifetime.
958 ///
959 /// # Safety
960 /// - `T` must be the erased pointee type for this [`MutUntyped`].
961 pub unsafe fn with_type<T>(self) -> Mut<'w, T> {
962 Mut {
963 // SAFETY: `value` is `Aligned` and caller ensures the pointee type is `T`.
964 value: unsafe { self.value.deref_mut() },
965 ticks: self.ticks,
966 }
967 }
968}
969
970impl<'w> DetectChanges for MutUntyped<'w> {
971 #[inline]
972 fn is_added(&self) -> bool {
973 self.ticks
974 .added
975 .is_newer_than(self.ticks.last_run, self.ticks.this_run)
976 }
977
978 #[inline]
979 fn is_changed(&self) -> bool {
980 self.ticks
981 .changed
982 .is_newer_than(self.ticks.last_run, self.ticks.this_run)
983 }
984
985 #[inline]
986 fn last_changed(&self) -> Tick {
987 *self.ticks.changed
988 }
989}
990
991impl<'w> DetectChangesMut for MutUntyped<'w> {
992 type Inner = PtrMut<'w>;
993
994 #[inline]
995 fn set_changed(&mut self) {
996 *self.ticks.changed = self.ticks.this_run;
997 }
998
999 #[inline]
1000 fn set_last_changed(&mut self, last_changed: Tick) {
1001 *self.ticks.changed = last_changed;
1002 }
1003
1004 #[inline]
1005 fn bypass_change_detection(&mut self) -> &mut Self::Inner {
1006 &mut self.value
1007 }
1008}
1009
1010impl std::fmt::Debug for MutUntyped<'_> {
1011 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1012 f.debug_tuple("MutUntyped")
1013 .field(&self.value.as_ptr())
1014 .finish()
1015 }
1016}
1017
1018impl<'w, T> From<Mut<'w, T>> for MutUntyped<'w> {
1019 fn from(value: Mut<'w, T>) -> Self {
1020 MutUntyped {
1021 value: value.value.into(),
1022 ticks: value.ticks,
1023 }
1024 }
1025}
1026
1027#[cfg(test)]
1028mod tests {
1029 use bevy_ecs_macros::Resource;
1030 use bevy_ptr::PtrMut;
1031 use bevy_reflect::{FromType, ReflectFromPtr};
1032 use std::ops::{Deref, DerefMut};
1033
1034 use crate::{
1035 self as bevy_ecs,
1036 change_detection::{
1037 Mut, NonSendMut, Ref, ResMut, TicksMut, CHECK_TICK_THRESHOLD, MAX_CHANGE_AGE,
1038 },
1039 component::{Component, ComponentTicks, Tick},
1040 system::{IntoSystem, Query, System},
1041 world::World,
1042 };
1043
1044 use super::{DetectChanges, DetectChangesMut, MutUntyped};
1045
1046 #[derive(Component, PartialEq)]
1047 struct C;
1048
1049 #[derive(Resource)]
1050 struct R;
1051
1052 #[derive(Resource, PartialEq)]
1053 struct R2(u8);
1054
1055 impl Deref for R2 {
1056 type Target = u8;
1057 fn deref(&self) -> &u8 {
1058 &self.0
1059 }
1060 }
1061
1062 impl DerefMut for R2 {
1063 fn deref_mut(&mut self) -> &mut u8 {
1064 &mut self.0
1065 }
1066 }
1067
1068 #[test]
1069 fn change_expiration() {
1070 fn change_detected(query: Query<Ref<C>>) -> bool {
1071 query.single().is_changed()
1072 }
1073
1074 fn change_expired(query: Query<Ref<C>>) -> bool {
1075 query.single().is_changed()
1076 }
1077
1078 let mut world = World::new();
1079
1080 // component added: 1, changed: 1
1081 world.spawn(C);
1082
1083 let mut change_detected_system = IntoSystem::into_system(change_detected);
1084 let mut change_expired_system = IntoSystem::into_system(change_expired);
1085 change_detected_system.initialize(&mut world);
1086 change_expired_system.initialize(&mut world);
1087
1088 // world: 1, system last ran: 0, component changed: 1
1089 // The spawn will be detected since it happened after the system "last ran".
1090 assert!(change_detected_system.run((), &mut world));
1091
1092 // world: 1 + MAX_CHANGE_AGE
1093 let change_tick = world.change_tick.get_mut();
1094 *change_tick = change_tick.wrapping_add(MAX_CHANGE_AGE);
1095
1096 // Both the system and component appeared `MAX_CHANGE_AGE` ticks ago.
1097 // Since we clamp things to `MAX_CHANGE_AGE` for determinism,
1098 // `ComponentTicks::is_changed` will now see `MAX_CHANGE_AGE > MAX_CHANGE_AGE`
1099 // and return `false`.
1100 assert!(!change_expired_system.run((), &mut world));
1101 }
1102
1103 #[test]
1104 fn change_tick_wraparound() {
1105 let mut world = World::new();
1106 world.last_change_tick = Tick::new(u32::MAX);
1107 *world.change_tick.get_mut() = 0;
1108
1109 // component added: 0, changed: 0
1110 world.spawn(C);
1111
1112 world.increment_change_tick();
1113
1114 // Since the world is always ahead, as long as changes can't get older than `u32::MAX` (which we ensure),
1115 // the wrapping difference will always be positive, so wraparound doesn't matter.
1116 let mut query = world.query::<Ref<C>>();
1117 assert!(query.single(&world).is_changed());
1118 }
1119
1120 #[test]
1121 fn change_tick_scan() {
1122 let mut world = World::new();
1123
1124 // component added: 1, changed: 1
1125 world.spawn(C);
1126
1127 // a bunch of stuff happens, the component is now older than `MAX_CHANGE_AGE`
1128 *world.change_tick.get_mut() += MAX_CHANGE_AGE + CHECK_TICK_THRESHOLD;
1129 let change_tick = world.change_tick();
1130
1131 let mut query = world.query::<Ref<C>>();
1132 for tracker in query.iter(&world) {
1133 let ticks_since_insert = change_tick.relative_to(*tracker.ticks.added).get();
1134 let ticks_since_change = change_tick.relative_to(*tracker.ticks.changed).get();
1135 assert!(ticks_since_insert > MAX_CHANGE_AGE);
1136 assert!(ticks_since_change > MAX_CHANGE_AGE);
1137 }
1138
1139 // scan change ticks and clamp those at risk of overflow
1140 world.check_change_ticks();
1141
1142 for tracker in query.iter(&world) {
1143 let ticks_since_insert = change_tick.relative_to(*tracker.ticks.added).get();
1144 let ticks_since_change = change_tick.relative_to(*tracker.ticks.changed).get();
1145 assert_eq!(ticks_since_insert, MAX_CHANGE_AGE);
1146 assert_eq!(ticks_since_change, MAX_CHANGE_AGE);
1147 }
1148 }
1149
1150 #[test]
1151 fn mut_from_res_mut() {
1152 let mut component_ticks = ComponentTicks {
1153 added: Tick::new(1),
1154 changed: Tick::new(2),
1155 };
1156 let ticks = TicksMut {
1157 added: &mut component_ticks.added,
1158 changed: &mut component_ticks.changed,
1159 last_run: Tick::new(3),
1160 this_run: Tick::new(4),
1161 };
1162 let mut res = R {};
1163 let res_mut = ResMut {
1164 value: &mut res,
1165 ticks,
1166 };
1167
1168 let into_mut: Mut<R> = res_mut.into();
1169 assert_eq!(1, into_mut.ticks.added.get());
1170 assert_eq!(2, into_mut.ticks.changed.get());
1171 assert_eq!(3, into_mut.ticks.last_run.get());
1172 assert_eq!(4, into_mut.ticks.this_run.get());
1173 }
1174
1175 #[test]
1176 fn mut_new() {
1177 let mut component_ticks = ComponentTicks {
1178 added: Tick::new(1),
1179 changed: Tick::new(3),
1180 };
1181 let mut res = R {};
1182
1183 let val = Mut::new(
1184 &mut res,
1185 &mut component_ticks.added,
1186 &mut component_ticks.changed,
1187 Tick::new(2), // last_run
1188 Tick::new(4), // this_run
1189 );
1190
1191 assert!(!val.is_added());
1192 assert!(val.is_changed());
1193 }
1194
1195 #[test]
1196 fn mut_from_non_send_mut() {
1197 let mut component_ticks = ComponentTicks {
1198 added: Tick::new(1),
1199 changed: Tick::new(2),
1200 };
1201 let ticks = TicksMut {
1202 added: &mut component_ticks.added,
1203 changed: &mut component_ticks.changed,
1204 last_run: Tick::new(3),
1205 this_run: Tick::new(4),
1206 };
1207 let mut res = R {};
1208 let non_send_mut = NonSendMut {
1209 value: &mut res,
1210 ticks,
1211 };
1212
1213 let into_mut: Mut<R> = non_send_mut.into();
1214 assert_eq!(1, into_mut.ticks.added.get());
1215 assert_eq!(2, into_mut.ticks.changed.get());
1216 assert_eq!(3, into_mut.ticks.last_run.get());
1217 assert_eq!(4, into_mut.ticks.this_run.get());
1218 }
1219
1220 #[test]
1221 fn map_mut() {
1222 use super::*;
1223 struct Outer(i64);
1224
1225 let last_run = Tick::new(2);
1226 let this_run = Tick::new(3);
1227 let mut component_ticks = ComponentTicks {
1228 added: Tick::new(1),
1229 changed: Tick::new(2),
1230 };
1231 let ticks = TicksMut {
1232 added: &mut component_ticks.added,
1233 changed: &mut component_ticks.changed,
1234 last_run,
1235 this_run,
1236 };
1237
1238 let mut outer = Outer(0);
1239 let ptr = Mut {
1240 value: &mut outer,
1241 ticks,
1242 };
1243 assert!(!ptr.is_changed());
1244
1245 // Perform a mapping operation.
1246 let mut inner = ptr.map_unchanged(|x| &mut x.0);
1247 assert!(!inner.is_changed());
1248
1249 // Mutate the inner value.
1250 *inner = 64;
1251 assert!(inner.is_changed());
1252 // Modifying one field of a component should flag a change for the entire component.
1253 assert!(component_ticks.is_changed(last_run, this_run));
1254 }
1255
1256 #[test]
1257 fn set_if_neq() {
1258 let mut world = World::new();
1259
1260 world.insert_resource(R2(0));
1261 // Resources are Changed when first added
1262 world.increment_change_tick();
1263 // This is required to update world::last_change_tick
1264 world.clear_trackers();
1265
1266 let mut r = world.resource_mut::<R2>();
1267 assert!(!r.is_changed(), "Resource must begin unchanged.");
1268
1269 r.set_if_neq(R2(0));
1270 assert!(
1271 !r.is_changed(),
1272 "Resource must not be changed after setting to the same value."
1273 );
1274
1275 r.set_if_neq(R2(3));
1276 assert!(
1277 r.is_changed(),
1278 "Resource must be changed after setting to a different value."
1279 );
1280 }
1281
1282 #[test]
1283 fn as_deref_mut() {
1284 let mut world = World::new();
1285
1286 world.insert_resource(R2(0));
1287 // Resources are Changed when first added
1288 world.increment_change_tick();
1289 // This is required to update world::last_change_tick
1290 world.clear_trackers();
1291
1292 let mut r = world.resource_mut::<R2>();
1293 assert!(!r.is_changed(), "Resource must begin unchanged.");
1294
1295 let mut r = r.as_deref_mut();
1296 assert!(
1297 !r.is_changed(),
1298 "Dereferencing should not mark the item as changed yet"
1299 );
1300
1301 r.set_if_neq(3);
1302 assert!(
1303 r.is_changed(),
1304 "Resource must be changed after setting to a different value."
1305 );
1306 }
1307
1308 #[test]
1309 fn mut_untyped_to_reflect() {
1310 let last_run = Tick::new(2);
1311 let this_run = Tick::new(3);
1312 let mut component_ticks = ComponentTicks {
1313 added: Tick::new(1),
1314 changed: Tick::new(2),
1315 };
1316 let ticks = TicksMut {
1317 added: &mut component_ticks.added,
1318 changed: &mut component_ticks.changed,
1319 last_run,
1320 this_run,
1321 };
1322
1323 let mut value: i32 = 5;
1324 let value = MutUntyped {
1325 value: PtrMut::from(&mut value),
1326 ticks,
1327 };
1328
1329 let reflect_from_ptr = <ReflectFromPtr as FromType<i32>>::from_type();
1330
1331 let mut new = value.map_unchanged(|ptr| {
1332 // SAFETY: The underlying type of `ptr` matches `reflect_from_ptr`.
1333 let value = unsafe { reflect_from_ptr.as_reflect_mut(ptr) };
1334 value
1335 });
1336
1337 assert!(!new.is_changed());
1338
1339 new.reflect_mut();
1340
1341 assert!(new.is_changed());
1342 }
1343
1344 #[test]
1345 fn mut_untyped_from_mut() {
1346 let mut component_ticks = ComponentTicks {
1347 added: Tick::new(1),
1348 changed: Tick::new(2),
1349 };
1350 let ticks = TicksMut {
1351 added: &mut component_ticks.added,
1352 changed: &mut component_ticks.changed,
1353 last_run: Tick::new(3),
1354 this_run: Tick::new(4),
1355 };
1356 let mut c = C {};
1357 let mut_typed = Mut {
1358 value: &mut c,
1359 ticks,
1360 };
1361
1362 let into_mut: MutUntyped = mut_typed.into();
1363 assert_eq!(1, into_mut.ticks.added.get());
1364 assert_eq!(2, into_mut.ticks.changed.get());
1365 assert_eq!(3, into_mut.ticks.last_run.get());
1366 assert_eq!(4, into_mut.ticks.this_run.get());
1367 }
1368}