Skip to main content

zr/
defer.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// A guard that executes an action when dropped, unless cancelled.
6///
7/// This provides equivalent functionality to C++ `fit::deferred_action` and `fit::defer`.
8#[must_use = "if unused the deferred action will execute immediately"]
9pub struct Deferred<F: FnOnce()> {
10    action: Option<F>,
11}
12
13impl<F: FnOnce()> Deferred<F> {
14    /// Creates a new `Deferred` action that will run when dropped.
15    #[inline]
16    pub const fn new(action: F) -> Self {
17        Self { action: Some(action) }
18    }
19
20    /// Cancels the deferred action so that it will not run upon drop.
21    #[inline]
22    pub fn cancel(&mut self) {
23        self.action = None;
24    }
25
26    /// Executes the deferred action immediately and cancels it.
27    #[inline]
28    pub fn call(&mut self) {
29        if let Some(action) = self.action.take() {
30            action();
31        }
32    }
33}
34
35impl<F: FnOnce()> Drop for Deferred<F> {
36    #[inline]
37    fn drop(&mut self) {
38        if let Some(action) = self.action.take() {
39            action();
40        }
41    }
42}
43
44/// Schedules an action to be executed when the returned guard is dropped.
45///
46/// If the returned guard is dropped, the closure `action` will be executed.
47/// Call [`Deferred::cancel`] on the returned guard to prevent execution.
48#[inline]
49pub fn defer<F: FnOnce()>(action: F) -> Deferred<F> {
50    Deferred::new(action)
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_defer_runs_on_drop() {
59        let mut executed = false;
60        {
61            let _guard = defer(|| {
62                executed = true;
63            });
64            assert!(!executed);
65        }
66        assert!(executed);
67    }
68
69    #[test]
70    fn test_defer_cancel() {
71        let mut executed = false;
72        {
73            let mut guard = defer(|| {
74                executed = true;
75            });
76            guard.cancel();
77        }
78        assert!(!executed);
79    }
80
81    #[test]
82    fn test_defer_call_early() {
83        let mut count = 0;
84        {
85            let mut guard = defer(|| {
86                count += 1;
87            });
88            assert_eq!(count, 0);
89            guard.call();
90            assert_eq!(count, 1);
91        }
92        // Should not run again on drop
93        assert_eq!(count, 1);
94    }
95}