|
| 1 | +--- |
| 2 | +sidebar_position: 3 |
| 3 | +--- |
| 4 | + |
| 5 | +# Deque |
| 6 | + |
| 7 | +A `Deque` is a container that imitates a traditional double-ended queue. It's designed for efficient |
| 8 | +pushes and pops from either the beginning or end of the queue, making it suitable for use as a queue or stack. |
| 9 | +However, it's not optimized for insertions or deletions from the middle. |
| 10 | + |
| 11 | +:::tip |
| 12 | + |
| 13 | +More information can be found in the [API docs]. |
| 14 | + |
| 15 | +::: |
| 16 | + |
| 17 | +## Deque operations |
| 18 | + |
| 19 | +The main operations available for a `Deque` are [`push_back`], [`push_front`], [`pop_back`], and |
| 20 | +[`pop_front`]. It is also possible to check the [`len`]gth of the deque, [`get`] an element by |
| 21 | +index, and [`iter`]ate over the elements. |
| 22 | + |
| 23 | +[`push_back`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.push_back |
| 24 | +[`push_front`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.push_front |
| 25 | +[`pop_back`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.pop_back |
| 26 | +[`pop_front`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.pop_front |
| 27 | +[`len`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.len |
| 28 | +[`get`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.get |
| 29 | +[`iter`]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html#method.iter |
| 30 | + |
| 31 | +- `push_back`: Adds an element to the end of the deque. O(1) time complexity. |
| 32 | +- `push_front`: Adds an element to the beginning of the deque. O(1) time complexity. |
| 33 | +- `pop_back`: Removes and returns the last element. Returns `None` if the deque is empty. O(1) time complexity. |
| 34 | +- `pop_front`: Removes and returns the first element. Returns `None` if the deque is empty. O(1) time complexity. |
| 35 | +- `len`: Returns the number of elements in the deque. O(1) time complexity. |
| 36 | +- `is_empty`: Returns `true` if the deque contains no elements. O(1) time complexity. |
| 37 | +- `get`: Retrieves an element by index. Returns `None` if the index is out of bounds. O(1) time complexity. |
| 38 | +- `front`: Returns a reference to the first element without removing it. Returns `None` if the deque is empty. |
| 39 | +- `back`: Returns a reference to the last element without removing it. Returns `None` if the deque is empty. |
| 40 | +- `iter`: Returns an iterator over the elements of the deque. |
| 41 | + |
| 42 | +:::warning |
| 43 | + |
| 44 | +The maximum capacity of a `Deque` is `u32::MAX - 1` elements. Attempting to push more elements |
| 45 | +is considered **Undefined Behavior**. |
| 46 | + |
| 47 | +::: |
| 48 | + |
| 49 | +## Deque lifecycle |
| 50 | + |
| 51 | +Creating a `Deque` doesn't immediately commit anything to storage. To add elements to the deque, you |
| 52 | +need to use the `push_back` or `push_front` methods. |
| 53 | + |
| 54 | +Values in a `Deque` must implement the `Serialize` and `Deserialize` traits from the [`serde`] crate |
| 55 | +to be stored. |
| 56 | + |
| 57 | +### Lifecycle example |
| 58 | + |
| 59 | +```rust |
| 60 | +use cw_storage_plus::Deque; |
| 61 | + |
| 62 | +// Create a new deque. It doesn't exist in storage yet. |
| 63 | +let deque: Deque<u32> = Deque::new("d"); |
| 64 | +assert_eq!(deque.len(&storage).unwrap(), 0); |
| 65 | + |
| 66 | +// Add elements to the deque |
| 67 | +deque.push_back(&mut storage, &2).unwrap(); |
| 68 | +deque.push_back(&mut storage, &3).unwrap(); |
| 69 | +deque.push_front(&mut storage, &1).unwrap(); |
| 70 | + |
| 71 | +// Check the length |
| 72 | +assert_eq!(deque.len(&storage).unwrap(), 3); |
| 73 | + |
| 74 | +// Remove elements |
| 75 | +assert_eq!(deque.pop_back(&mut storage).unwrap(), Some(3)); |
| 76 | +assert_eq!(deque.pop_front(&mut storage).unwrap(), Some(1)); |
| 77 | + |
| 78 | +// Check the final state |
| 79 | +assert_eq!(deque.len(&storage).unwrap(), 1); |
| 80 | +assert_eq!(deque.get(&storage, 0).unwrap(), Some(2)); |
| 81 | +``` |
| 82 | + |
| 83 | +## Usage examples |
| 84 | + |
| 85 | +### Using Deque as a queue |
| 86 | + |
| 87 | +```rust |
| 88 | +use cw_storage_plus::Deque; |
| 89 | + |
| 90 | +let queue: Deque<String> = Deque::new("q"); |
| 91 | + |
| 92 | +// Enqueue elements |
| 93 | +queue.push_back(&mut storage, &"first".to_string()).unwrap(); |
| 94 | +queue.push_back(&mut storage, &"second".to_string()).unwrap(); |
| 95 | + |
| 96 | +// Dequeue elements |
| 97 | +assert_eq!(queue.pop_front(&mut storage).unwrap(), Some("first".to_string())); |
| 98 | +assert_eq!(queue.pop_front(&mut storage).unwrap(), Some("second".to_string())); |
| 99 | +assert_eq!(queue.pop_front(&mut storage).unwrap(), None); |
| 100 | +``` |
| 101 | + |
| 102 | +### Using Deque as a stack |
| 103 | + |
| 104 | +```rust |
| 105 | +use cw_storage_plus::Deque; |
| 106 | + |
| 107 | +let stack: Deque<u32> = Deque::new("s"); |
| 108 | + |
| 109 | +// Push elements |
| 110 | +stack.push_back(&mut storage, &1).unwrap(); |
| 111 | +stack.push_back(&mut storage, &2).unwrap(); |
| 112 | +stack.push_back(&mut storage, &3).unwrap(); |
| 113 | + |
| 114 | +// Pop elements |
| 115 | +assert_eq!(stack.pop_back(&mut storage).unwrap(), Some(3)); |
| 116 | +assert_eq!(stack.pop_back(&mut storage).unwrap(), Some(2)); |
| 117 | +assert_eq!(stack.pop_back(&mut storage).unwrap(), Some(1)); |
| 118 | +assert_eq!(stack.pop_back(&mut storage).unwrap(), None); |
| 119 | +``` |
| 120 | + |
| 121 | +### Iterating over elements |
| 122 | + |
| 123 | +```rust |
| 124 | +use cw_storage_plus::Deque; |
| 125 | + |
| 126 | +let deque: Deque<u32> = Deque::new("d"); |
| 127 | + |
| 128 | +deque.push_back(&mut storage, &1).unwrap(); |
| 129 | +deque.push_back(&mut storage, &2).unwrap(); |
| 130 | +deque.push_back(&mut storage, &3).unwrap(); |
| 131 | + |
| 132 | +let sum: u32 = deque.iter(&storage).unwrap().map(|r| r.unwrap()).sum(); |
| 133 | +assert_eq!(sum, 6); |
| 134 | +``` |
| 135 | + |
| 136 | +### Maintaining a log store |
| 137 | + |
| 138 | +It is possible to use a `Deque` to maintain a store of log events or transaction records. This is |
| 139 | +useful when you want to keep a history of production level events to ease in debugging a deployed |
| 140 | +instance of a contract. |
| 141 | + |
| 142 | +```rust |
| 143 | +use cw_storage_plus::Deque; |
| 144 | +use serde::{Serialize, Deserialize}; |
| 145 | +use cosmwasm_std::testing::*; |
| 146 | +use cosmwasm_std::Addr; |
| 147 | + |
| 148 | +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] |
| 149 | +struct Log { |
| 150 | + block_height: u64, |
| 151 | + sender_addr: String, |
| 152 | + event: String, |
| 153 | +} |
| 154 | + |
| 155 | +// Initialize the Deque for storing logs |
| 156 | +let logs: Deque<Log> = Deque::new("logs"); |
| 157 | + |
| 158 | +// Simulating contract execution context |
| 159 | +let env = mock_env(); |
| 160 | +let info = message_info(&Addr::unchecked("sender"), &[]); |
| 161 | +let event = "funds_transferred".to_string(); |
| 162 | + |
| 163 | +// Add a new log entry |
| 164 | +logs.push_front( |
| 165 | + &mut storage, |
| 166 | + &Log { |
| 167 | + block_height: env.block.height, |
| 168 | + sender_addr: info.sender.to_string(), |
| 169 | + event: event.clone(), |
| 170 | + }, |
| 171 | +).unwrap(); |
| 172 | + |
| 173 | +// Optionally, limit the number of stored logs |
| 174 | +const MAX_LOGS: u32 = 100; |
| 175 | +if logs.len(&storage).unwrap() > MAX_LOGS { |
| 176 | + logs.pop_back(&mut storage).unwrap(); |
| 177 | +} |
| 178 | + |
| 179 | +// Retrieve the most recent log |
| 180 | +let latest_log = logs.get(&storage, 0).unwrap().unwrap(); |
| 181 | +assert_eq!(latest_log.event, "funds_transferred"); |
| 182 | +``` |
| 183 | + |
| 184 | +[`serde`]: https://serde.rs/ |
| 185 | +[API docs]: https://docs.rs/cw-storage-plus/latest/cw_storage_plus/struct.Deque.html |
0 commit comments