1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
//! A library for creating debug-only tags to track and check where values are used. //! //! Useful for asserting that two values stem from the same origin. For example, an operation on two //! nodes may only be valid if the nodes are in the same graph. `DebugTag` can be used to check for //! this in tests and when debugging. //! //! See the `DebugTag` type for more details. //! //! # Motivation //! //! When developing Rust, we often use *indices* to refer to other values instead of using //! *pointers*. Using indices allows us to "point" to a value without borrowing it and can basically //! be seen as bypassing the borrow checker in a restricted way. //! //! One of the issues of using indicies, is that it is not connected at compile time to the //! container storing the value. An index used with an incorrect container might panic, or worse, //! return garbage values. //! //! We need a way to "tag" values such that we can determine if an index is used with the correct //! container. //! //! # Example //! //! ``` //! use debug_tag::DebugTag; //! //! /// An example vec-like structure that allows pushing values and getting values by index. //! #[derive(Default)] //! struct Slab<T>{ //! items: Vec<T>, //! tag: DebugTag, //! } //! //! /// An index into a value on a slab. //! #[derive(Copy, Clone)] //! struct Index { //! index: usize, //! tag: DebugTag, //! } //! //! impl<T> Slab<T> { //! /// Pushes a new value onto the slab. //! fn push(&mut self, item: T) -> Index { //! let index = self.items.len(); //! self.items.push(item); //! Index { //! index, //! tag: self.tag //! } //! } //! //! /// Gets a value in this `Slab`. If the index is not from this slab, either panics or //! /// returns an arbitrary value. //! fn get(&self, index: Index) -> &T { //! assert_eq!(self.tag, index.tag, "Index must stem from this slab"); //! &self.items[index.index] //! } //! } //! //! let mut slab_a = Slab::default(); //! let ix = slab_a.push(42); //! //! assert_eq!(slab_a.get(ix), &42); //! //! let mut slab_b = Slab::default(); //! slab_b.push(1337); //! //! // Panics due to the tags being checked: //! // assert_eq!(slab_b.get(ix), &1337); //! ``` //! //! Without `DebugTag`s, the last line above would just return 1337 - a "garbage value" since `ix` //! stems from a different `Slab`. #[cfg(debug_assertions)] mod checked { use std::cell::Cell; use std::sync::atomic::{AtomicU32, Ordering}; // The increment to global every time we fetch a local tag offset. This is equal to // 2**32 * (1 - 1/(golden ratio)), which ends up distributing offsets well for an arbitrary // number of local threads. const INCREMENT: u32 = 1_640_531_527; static GLOBAL: AtomicU32 = AtomicU32::new(INCREMENT); thread_local! { static LOCAL: Cell<u32> = Cell::new(GLOBAL.fetch_add(INCREMENT, Ordering::SeqCst)); } pub fn next() -> u32 { LOCAL.with(|local| { let old = local.get(); local.set(old.wrapping_add(1)); old }) } } /// A value that guarentees that if two `DebugTag`s are not equal, then they are *not* clones. /// /// This can be used to tag information during debug, such that the use of a value can be tracked /// and checked. For example, you can use this to ensure (in debug) that a value returned by a data /// structure is only used with the instance that returned the value. /// /// This tagging is only done if `debug_assertions` is set. If `debug_assertions` is not set, then /// all `DebugTags` are equal. Even if `debug_assertions` is set, two `DebugTag`s that are not /// clones can still be equal. This is unlikely, however. /// /// Therefore, functionality should not directly depend on the equality these tags but only use them /// for additional sanity checks. #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[non_exhaustive] pub struct DebugTag( #[cfg(debug_assertions)] u32, ); impl Default for DebugTag { fn default() -> DebugTag { #[cfg(debug_assertions)] let tag = DebugTag(checked::next()); #[cfg(not(debug_assertions))] let tag = DebugTag(); tag } } impl DebugTag { /// Creates a new `DebugTag` pub fn new() -> DebugTag { DebugTag::default() } /// Create a new tag with the specified value. /// /// Prefer using `new` instead, which will generate a value. Use this only in cases where that /// is not possible, like when creating a const debug tag. /// /// The tag value should be a randomly chosen constant. pub const fn from(_tag: u32) -> DebugTag { #[cfg(debug_assertions)] let tag = DebugTag(_tag); #[cfg(not(debug_assertions))] let tag = DebugTag(); tag } } #[cfg(test)] mod tests { use super::DebugTag; #[test] #[cfg(debug_assertions)] fn not_equal() { let a = DebugTag::new(); let b = DebugTag::new(); assert!(a != b); } #[test] fn equal() { let a = DebugTag::new(); let b = a.clone(); assert!(a == b); } #[test] fn reflexive() { let a = DebugTag::new(); assert!(a == a); } }