Skip to content

Commit 4ba88d3

Browse files
committed
Add map/map_mut/extract_inner methods
1 parent 76e7657 commit 4ba88d3

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed

src/lib.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,147 @@ impl<R> Volatile<R> {
158158
}
159159
}
160160

161+
/// Method for extracting the wrapped value.
162+
impl<R, A> Volatile<R, A> {
163+
/// Extracts the inner value stored in the wrapper type.
164+
///
165+
/// This method gives direct access to the wrapped reference and thus allows
166+
/// non-volatile access again. This is seldom what you want since there is usually
167+
/// a reason that a reference is wrapped in `Volatile`. However, in some cases it might
168+
/// be required or useful to use the `read_volatile`/`write_volatile` pointer methods of
169+
/// the standard library directly, which this method makes possible.
170+
///
171+
/// Since no memory safety violation can occur when accessing the referenced value using
172+
/// non-volatile operations, this method is safe. However, it _can_ lead to bugs at the
173+
/// application level, so this method should be used with care.
174+
///
175+
/// ## Example
176+
///
177+
/// ```
178+
/// use volatile::Volatile;
179+
///
180+
/// let mut value = 42;
181+
/// let mut volatile = Volatile::new(&mut value);
182+
/// volatile.write(50);
183+
/// let unwrapped: &mut i32 = volatile.extract_inner();
184+
///
185+
/// assert_eq!(*unwrapped, 50); // non volatile access, be careful!
186+
/// ```
187+
pub fn extract_inner(self) -> R {
188+
self.reference
189+
}
190+
}
191+
192+
/// Transformation methods for accessing struct fields
193+
impl<R, T, A> Volatile<R, A>
194+
where
195+
R: Deref<Target = T>,
196+
T: ?Sized,
197+
{
198+
/// Constructs a new `Volatile` reference by mapping the wrapped value.
199+
///
200+
/// This method is useful for accessing individual fields of volatile structs.
201+
///
202+
/// Note that this method gives temporary access to the wrapped reference, which allows
203+
/// accessing the value in a non-volatile way. This is normally not what you want, so
204+
/// **this method should only be used for reference-to-reference transformations**.
205+
///
206+
/// ## Examples
207+
///
208+
/// Accessing a struct field:
209+
///
210+
/// ```
211+
/// use volatile::Volatile;
212+
///
213+
/// struct Example { field_1: u32, field_2: u8, }
214+
/// let mut value = Example { field_1: 15, field_2: 255 };
215+
/// let mut volatile = Volatile::new(&mut value);
216+
///
217+
/// // construct a volatile reference to a field
218+
/// let field_2 = volatile.map(|example| &example.field_2);
219+
/// assert_eq!(field_2.read(), 255);
220+
/// ```
221+
///
222+
/// Don't misuse this method to do a non-volatile read of the referenced value:
223+
///
224+
/// ```
225+
/// use volatile::Volatile;
226+
///
227+
/// let mut value = 5;
228+
/// let mut volatile = Volatile::new(&mut value);
229+
///
230+
/// // DON'T DO THIS:
231+
/// let mut readout = 0;
232+
/// volatile.map(|value| {
233+
/// readout = *value; // non-volatile read, might lead to bugs
234+
/// value
235+
/// });
236+
/// ```
237+
pub fn map<'a, F, U>(&'a self, f: F) -> Volatile<&'a U, A>
238+
where
239+
F: FnOnce(&'a T) -> &'a U,
240+
U: ?Sized,
241+
T: 'a,
242+
{
243+
Volatile {
244+
reference: f(self.reference.deref()),
245+
access: self.access,
246+
}
247+
}
248+
249+
/// Constructs a new mutable `Volatile` reference by mapping the wrapped value.
250+
///
251+
/// This method is useful for accessing individual fields of volatile structs.
252+
///
253+
/// Note that this method gives temporary access to the wrapped reference, which allows
254+
/// accessing the value in a non-volatile way. This is normally not what you want, so
255+
/// **this method should only be used for reference-to-reference transformations**.
256+
///
257+
/// ## Examples
258+
///
259+
/// Accessing a struct field:
260+
///
261+
/// ```
262+
/// use volatile::Volatile;
263+
///
264+
/// struct Example { field_1: u32, field_2: u8, }
265+
/// let mut value = Example { field_1: 15, field_2: 255 };
266+
/// let mut volatile = Volatile::new(&mut value);
267+
///
268+
/// // construct a volatile reference to a field
269+
/// let mut field_2 = volatile.map_mut(|example| &mut example.field_2);
270+
/// field_2.write(128);
271+
/// assert_eq!(field_2.read(), 128);
272+
/// ```
273+
///
274+
/// Don't misuse this method to do a non-volatile read or write of the referenced value:
275+
///
276+
/// ```
277+
/// use volatile::Volatile;
278+
///
279+
/// let mut value = 5;
280+
/// let mut volatile = Volatile::new(&mut value);
281+
///
282+
/// // DON'T DO THIS:
283+
/// volatile.map_mut(|value| {
284+
/// *value = 10; // non-volatile write, might lead to bugs
285+
/// value
286+
/// });
287+
/// ```
288+
pub fn map_mut<'a, F, U>(&'a mut self, f: F) -> Volatile<&'a mut U, A>
289+
where
290+
F: FnOnce(&mut T) -> &mut U,
291+
R: DerefMut,
292+
U: ?Sized,
293+
T: 'a,
294+
{
295+
Volatile {
296+
reference: f(&mut self.reference),
297+
access: self.access,
298+
}
299+
}
300+
}
301+
161302
/// Methods for references to `Copy` types
162303
impl<R, T, A> Volatile<R, A>
163304
where
@@ -501,4 +642,24 @@ mod tests {
501642
volatile.index_mut(0).update(|v| *v += 1);
502643
assert_eq!(val, [2, 2, 3]);
503644
}
645+
646+
#[test]
647+
fn test_struct() {
648+
struct S {
649+
field_1: u32,
650+
field_2: bool,
651+
}
652+
653+
let mut val = S {
654+
field_1: 60,
655+
field_2: true,
656+
};
657+
let mut volatile = Volatile::new(&mut val);
658+
volatile.map_mut(|s| &mut s.field_1).update(|v| *v += 1);
659+
let mut field_2 = volatile.map_mut(|s| &mut s.field_2);
660+
assert!(field_2.read());
661+
field_2.write(false);
662+
assert_eq!(volatile.map(|s| &s.field_1).read(), 61);
663+
assert_eq!(volatile.map(|s| &s.field_2).read(), false);
664+
}
504665
}

0 commit comments

Comments
 (0)