redoubt_alloc/
redoubt_option.rs

1// Copyright (c) 2025-2026 Federico Hoerth <memparanoid@gmail.com>
2// SPDX-License-Identifier: GPL-3.0-only
3// See LICENSE in the repository root for full license text.
4
5use redoubt_zero::{
6    FastZeroizable, RedoubtZero, ZeroizationProbe, ZeroizeMetadata, ZeroizeOnDropSentinel,
7};
8
9use crate::error::RedoubtOptionError;
10
11/// An optional value wrapper with automatic zeroization.
12#[derive(RedoubtZero, Default)]
13pub struct RedoubtOption<T>
14where
15    T: FastZeroizable + ZeroizeMetadata + ZeroizationProbe,
16{
17    inner: Option<T>,
18    __sentinel: ZeroizeOnDropSentinel,
19}
20
21impl<T> RedoubtOption<T>
22where
23    T: FastZeroizable + ZeroizeMetadata + ZeroizationProbe,
24{
25    /// Returns a reference to the inner value, or an error if `None`.
26    pub fn as_ref(&self) -> Result<&T, RedoubtOptionError> {
27        self.inner.as_ref().ok_or(RedoubtOptionError::Empty)
28    }
29
30    /// Returns a mutable reference to the inner value, or an error if `None`.
31    pub fn as_mut(&mut self) -> Result<&mut T, RedoubtOptionError> {
32        self.inner.as_mut().ok_or(RedoubtOptionError::Empty)
33    }
34
35    /// Replaces the inner value with a new one, zeroizing both the old value and the source.
36    pub fn replace(&mut self, value: &mut T)
37    where
38        T: Default,
39    {
40        // Zeroize old value if Some
41        if let Some(old) = &mut self.inner {
42            old.fast_zeroize();
43        }
44
45        // Move new value from source
46        let mut new_value = T::default();
47        unsafe {
48            // SAFETY: Both pointers are valid and properly aligned
49            core::ptr::swap_nonoverlapping(value, &mut new_value, 1);
50        }
51        self.inner = Some(new_value);
52
53        // Zeroize source
54        value.fast_zeroize();
55    }
56
57    /// Takes the value out of the option, leaving `None` in its place.
58    pub fn take(&mut self) -> Result<T, RedoubtOptionError> {
59        self.inner.take().ok_or(RedoubtOptionError::Empty)
60    }
61
62    /// Returns `true` if the option contains a value.
63    pub fn is_some(&self) -> bool {
64        self.inner.is_some()
65    }
66
67    /// Returns `true` if the option is `None`.
68    pub fn is_none(&self) -> bool {
69        self.inner.is_none()
70    }
71
72    /// Returns a reference to the inner `Option<T>`.
73    #[inline(always)]
74    pub fn as_option(&self) -> &Option<T> {
75        &self.inner
76    }
77
78    /// Returns a mutable reference to the inner `Option<T>`.
79    #[inline(always)]
80    pub fn as_mut_option(&mut self) -> &mut Option<T> {
81        &mut self.inner
82    }
83}