Skip to main content

truthlinked_sdk/
collections.rs

1//! High-level storage collections built on 32-byte slots.
2//!
3//! This module provides three storage collection types that abstract over raw slot operations:
4//!
5//! - **`StorageMap<V>`**: Key-value map (like `HashMap`)
6//! - **`StorageVec<V>`**: Dynamic array (like `Vec`)
7//! - **`StorageBlob`**: Variable-length byte storage
8//!
9//! All collections use namespace isolation to prevent slot collisions and support
10//! both production (`HostStorage`) and testing (`MemoryStorage`) backends.
11//!
12//! # Namespace Isolation
13//!
14//! Each collection requires a unique namespace (32-byte identifier) to isolate
15//! its storage slots from other collections:
16//!
17//! ```ignore
18//! const NS_BALANCES: Namespace = Namespace([1u8; 32]);
19//! const NS_ALLOWANCES: Namespace = Namespace([2u8; 32]);
20//!
21//! let balances = StorageMap::<u64>::new(NS_BALANCES);
22//! let allowances = StorageMap::<u64>::new(NS_ALLOWANCES);
23//! ```
24//!
25//! # Dual API Pattern
26//!
27//! Each collection provides two API surfaces:
28//!
29//! - **`_in()` methods**: Accept explicit backend parameter (for testing)
30//! - **Regular methods**: Use `HostStorage` implicitly (for production)
31//!
32//! ```ignore
33//! // Production: uses HostStorage
34//! map.insert(b"key", &value)?;
35//!
36//! // Testing: explicit backend
37//! let mut storage = MemoryStorage::new();
38//! map.insert_in(&mut storage, b"key", &value)?;
39//! ```
40//!
41//! # Example: Token Balances
42//!
43//! ```ignore
44//! use truthlinked_sdk::collections::{Namespace, StorageMap};
45//!
46//! const NS_BALANCES: Namespace = Namespace([1u8; 32]);
47//!
48//! fn transfer(from: [u8; 32], to: [u8; 32], amount: u64) -> Result<()> {
49//!     let balances = StorageMap::<u64>::new(NS_BALANCES);
50//!     
51//!     let from_balance = balances.get_typed_key(&from)?.unwrap_or(0);
52//!     let to_balance = balances.get_typed_key(&to)?.unwrap_or(0);
53//!     
54//!     if from_balance < amount {
55//!         return Err(Error::new(ERR_INSUFFICIENT_BALANCE));
56//!     }
57//!     
58//!     balances.insert_typed_key(&from, &(from_balance - amount))?;
59//!     balances.insert_typed_key(&to, &(to_balance + amount))?;
60//!     
61//!     Ok(())
62//! }
63//! ```
64
65extern crate alloc;
66
67use alloc::vec;
68use alloc::vec::Vec;
69use core::marker::PhantomData;
70
71use crate::backend::{HostStorage, StorageBackend};
72use crate::codec::{BytesCodec, Codec32};
73use crate::error::Result;
74use crate::hashing;
75
76/// A 32-byte namespace identifier for storage isolation.
77///
78/// Namespaces prevent slot collisions between different collections.
79/// Each collection should use a unique namespace.
80///
81/// # Example
82///
83/// ```ignore
84/// const NS_BALANCES: Namespace = Namespace([1u8; 32]);
85/// const NS_ALLOWANCES: Namespace = Namespace([2u8; 32]);
86/// ```
87#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
88pub struct Namespace(pub [u8; 32]);
89
90impl Namespace {
91    /// Creates a new namespace from a 32-byte array.
92    pub const fn new(bytes: [u8; 32]) -> Self {
93        Self(bytes)
94    }
95}
96
97/// A persistent key-value map stored in contract storage.
98///
99/// `StorageMap` provides a `HashMap`-like interface over contract storage slots.
100/// Each entry uses two slots: one for existence check, one for the value.
101///
102/// # Type Parameters
103///
104/// * `V` - Value type, must implement `Codec32` for 32-byte encoding
105///
106/// # Storage Layout
107///
108/// For each key, two slots are derived:
109/// - `derive_slot(namespace, ["map:exists", key])` - Existence flag (1 byte)
110/// - `derive_slot(namespace, ["map:value", key])` - Encoded value (32 bytes)
111///
112/// # Example
113///
114/// ```ignore
115/// const NS_BALANCES: Namespace = Namespace([1u8; 32]);
116/// let balances = StorageMap::<u64>::new(NS_BALANCES);
117///
118/// // Insert
119/// balances.insert(b"alice", &1000)?;
120///
121/// // Get
122/// let balance = balances.get(b"alice")?.unwrap_or(0);
123///
124/// // Check existence
125/// if balances.contains_key(b"alice")? {
126///     // Key exists
127/// }
128///
129/// // Remove
130/// balances.remove(b"alice")?;
131/// ```
132pub struct StorageMap<V: Codec32> {
133    namespace: Namespace,
134    _marker: PhantomData<V>,
135}
136
137impl<V: Codec32> StorageMap<V> {
138    /// Creates a new map with the specified namespace.
139    ///
140    /// # Arguments
141    ///
142    /// * `namespace` - Unique 32-byte identifier for this map
143    pub const fn new(namespace: Namespace) -> Self {
144        Self {
145            namespace,
146            _marker: PhantomData,
147        }
148    }
149
150    /// Computes the existence slot for a given key.
151    ///
152    /// Returns the storage slot address where the existence flag is stored.
153    pub fn exists_slot_for(&self, key: &[u8]) -> [u8; 32] {
154        hashing::derive_slot(&self.namespace.0, &[b"map:exists", key])
155    }
156
157    /// Computes the value slot for a given key.
158    ///
159    /// Returns the storage slot address where the encoded value is stored.
160    pub fn value_slot_for(&self, key: &[u8]) -> [u8; 32] {
161        hashing::derive_slot(&self.namespace.0, &[b"map:value", key])
162    }
163
164    /// Returns both slots (existence, value) for a given key.
165    ///
166    /// Useful for manifest generation.
167    pub fn slots_for_key(&self, key: &[u8]) -> ([u8; 32], [u8; 32]) {
168        (self.exists_slot_for(key), self.value_slot_for(key))
169    }
170
171    /// Checks if a key exists in the map (with explicit backend).
172    pub fn contains_key_in<B: StorageBackend>(&self, backend: &B, key: &[u8]) -> Result<bool> {
173        let exists = backend.read_32(&self.exists_slot_for(key))?;
174        Ok(exists[0] == 1)
175    }
176
177    /// Gets the value for a key (with explicit backend).
178    ///
179    /// Returns `None` if the key doesn't exist.
180    pub fn get_in<B: StorageBackend>(&self, backend: &B, key: &[u8]) -> Result<Option<V>> {
181        if !self.contains_key_in(backend, key)? {
182            return Ok(None);
183        }
184        let raw = backend.read_32(&self.value_slot_for(key))?;
185        Ok(Some(V::decode_32(&raw)?))
186    }
187
188    /// Inserts a key-value pair (with explicit backend).
189    pub fn insert_in<B: StorageBackend>(
190        &self,
191        backend: &mut B,
192        key: &[u8],
193        value: &V,
194    ) -> Result<()> {
195        let mut exists = [0u8; 32];
196        exists[0] = 1;
197        backend.write_32(self.exists_slot_for(key), exists)?;
198        backend.write_32(self.value_slot_for(key), value.encode_32())?;
199        Ok(())
200    }
201
202    /// Removes a key-value pair (with explicit backend).
203    pub fn remove_in<B: StorageBackend>(&self, backend: &mut B, key: &[u8]) -> Result<()> {
204        backend.write_32(self.exists_slot_for(key), [0u8; 32])?;
205        backend.write_32(self.value_slot_for(key), [0u8; 32])?;
206        Ok(())
207    }
208
209    /// Checks if a typed key exists (with explicit backend).
210    ///
211    /// The key is encoded using `BytesCodec` before lookup.
212    pub fn contains_typed_key_in<B: StorageBackend, K: BytesCodec>(
213        &self,
214        backend: &B,
215        key: &K,
216    ) -> Result<bool> {
217        let key_bytes = key.encode_bytes();
218        self.contains_key_in(backend, &key_bytes)
219    }
220
221    /// Gets the value for a typed key (with explicit backend).
222    pub fn get_typed_key_in<B: StorageBackend, K: BytesCodec>(
223        &self,
224        backend: &B,
225        key: &K,
226    ) -> Result<Option<V>> {
227        let key_bytes = key.encode_bytes();
228        self.get_in(backend, &key_bytes)
229    }
230
231    /// Inserts a typed key-value pair (with explicit backend).
232    pub fn insert_typed_key_in<B: StorageBackend, K: BytesCodec>(
233        &self,
234        backend: &mut B,
235        key: &K,
236        value: &V,
237    ) -> Result<()> {
238        let key_bytes = key.encode_bytes();
239        self.insert_in(backend, &key_bytes, value)
240    }
241
242    /// Removes a typed key (with explicit backend).
243    pub fn remove_typed_key_in<B: StorageBackend, K: BytesCodec>(
244        &self,
245        backend: &mut B,
246        key: &K,
247    ) -> Result<()> {
248        let key_bytes = key.encode_bytes();
249        self.remove_in(backend, &key_bytes)
250    }
251
252    /// Checks if a key exists (production, uses `HostStorage`).
253    pub fn contains_key(&self, key: &[u8]) -> Result<bool> {
254        let host = HostStorage;
255        self.contains_key_in(&host, key)
256    }
257
258    /// Gets the value for a key (production, uses `HostStorage`).
259    pub fn get(&self, key: &[u8]) -> Result<Option<V>> {
260        let host = HostStorage;
261        self.get_in(&host, key)
262    }
263
264    /// Inserts a key-value pair (production, uses `HostStorage`).
265    pub fn insert(&self, key: &[u8], value: &V) -> Result<()> {
266        let mut host = HostStorage;
267        self.insert_in(&mut host, key, value)
268    }
269
270    /// Removes a key-value pair (production, uses `HostStorage`).
271    pub fn remove(&self, key: &[u8]) -> Result<()> {
272        let mut host = HostStorage;
273        self.remove_in(&mut host, key)
274    }
275
276    /// Checks if a typed key exists (production, uses `HostStorage`).
277    pub fn contains_typed_key<K: BytesCodec>(&self, key: &K) -> Result<bool> {
278        let host = HostStorage;
279        self.contains_typed_key_in(&host, key)
280    }
281
282    /// Gets the value for a typed key (production, uses `HostStorage`).
283    pub fn get_typed_key<K: BytesCodec>(&self, key: &K) -> Result<Option<V>> {
284        let host = HostStorage;
285        self.get_typed_key_in(&host, key)
286    }
287
288    /// Inserts a typed key-value pair (production, uses `HostStorage`).
289    pub fn insert_typed_key<K: BytesCodec>(&self, key: &K, value: &V) -> Result<()> {
290        let mut host = HostStorage;
291        self.insert_typed_key_in(&mut host, key, value)
292    }
293
294    /// Removes a typed key (production, uses `HostStorage`).
295    pub fn remove_typed_key<K: BytesCodec>(&self, key: &K) -> Result<()> {
296        let mut host = HostStorage;
297        self.remove_typed_key_in(&mut host, key)
298    }
299}
300
301/// A persistent dynamic array stored in contract storage.
302///
303/// `StorageVec` provides a `Vec`-like interface over contract storage slots.
304/// It stores a length counter and individual elements at derived slot addresses.
305///
306/// # Type Parameters
307///
308/// * `V` - Element type, must implement `Codec32` for 32-byte encoding
309///
310/// # Storage Layout
311///
312/// - `derive_slot(namespace, ["vec:len"])` - Length as u64
313/// - `derive_slot(namespace, ["vec:elem", index])` - Element at index
314///
315/// # Example
316///
317/// ```ignore
318/// const NS_HISTORY: Namespace = Namespace([1u8; 32]);
319/// let history = StorageVec::<u64>::new(NS_HISTORY);
320///
321/// // Push elements
322/// history.push(&100)?;
323/// history.push(&200)?;
324///
325/// // Get length
326/// let len = history.len()?; // 2
327///
328/// // Get element
329/// let first = history.get(0)?.unwrap(); // 100
330///
331/// // Pop element
332/// let last = history.pop()?.unwrap(); // 200
333/// ```
334pub struct StorageVec<V: Codec32> {
335    namespace: Namespace,
336    _marker: PhantomData<V>,
337}
338
339impl<V: Codec32> StorageVec<V> {
340    /// Creates a new vector with the specified namespace.
341    pub const fn new(namespace: Namespace) -> Self {
342        Self {
343            namespace,
344            _marker: PhantomData,
345        }
346    }
347
348    /// Returns the storage slot for the length counter.
349    pub fn len_slot(&self) -> [u8; 32] {
350        hashing::derive_slot(&self.namespace.0, &[b"vec:len"])
351    }
352
353    /// Returns the storage slot for an element at the given index.
354    pub fn slot_for_index(&self, index: u64) -> [u8; 32] {
355        let idx = hashing::index_u64(index);
356        hashing::derive_slot(&self.namespace.0, &[b"vec:elem", &idx])
357    }
358
359    /// Returns the number of elements (with explicit backend).
360    pub fn len_in<B: StorageBackend>(&self, backend: &B) -> Result<u64> {
361        let raw = backend.read_32(&self.len_slot())?;
362        <u64 as Codec32>::decode_32(&raw)
363    }
364
365    /// Checks if the vector is empty (with explicit backend).
366    pub fn is_empty_in<B: StorageBackend>(&self, backend: &B) -> Result<bool> {
367        Ok(self.len_in(backend)? == 0)
368    }
369
370    /// Gets the element at index (with explicit backend).
371    ///
372    /// Returns `None` if index is out of bounds.
373    pub fn get_in<B: StorageBackend>(&self, backend: &B, index: u64) -> Result<Option<V>> {
374        let len = self.len_in(backend)?;
375        if index >= len {
376            return Ok(None);
377        }
378        let raw = backend.read_32(&self.slot_for_index(index))?;
379        Ok(Some(V::decode_32(&raw)?))
380    }
381
382    /// Sets the element at index (with explicit backend).
383    ///
384    /// Returns `false` if index is out of bounds.
385    pub fn set_in<B: StorageBackend>(
386        &self,
387        backend: &mut B,
388        index: u64,
389        value: &V,
390    ) -> Result<bool> {
391        let len = self.len_in(backend)?;
392        if index >= len {
393            return Ok(false);
394        }
395        backend.write_32(self.slot_for_index(index), value.encode_32())?;
396        Ok(true)
397    }
398
399    /// Appends an element to the end (with explicit backend).
400    ///
401    /// Returns the index where the element was inserted.
402    pub fn push_in<B: StorageBackend>(&self, backend: &mut B, value: &V) -> Result<u64> {
403        let len = self.len_in(backend)?;
404        backend.write_32(self.slot_for_index(len), value.encode_32())?;
405        backend.write_32(self.len_slot(), (len + 1).encode_32())?;
406        Ok(len)
407    }
408
409    /// Removes and returns the last element (with explicit backend).
410    ///
411    /// Returns `None` if the vector is empty.
412    pub fn pop_in<B: StorageBackend>(&self, backend: &mut B) -> Result<Option<V>> {
413        let len = self.len_in(backend)?;
414        if len == 0 {
415            return Ok(None);
416        }
417        let index = len - 1;
418        let raw = backend.read_32(&self.slot_for_index(index))?;
419        backend.write_32(self.len_slot(), index.encode_32())?;
420        backend.write_32(self.slot_for_index(index), [0u8; 32])?;
421        Ok(Some(V::decode_32(&raw)?))
422    }
423
424    /// Clears the vector by setting length to 0 (with explicit backend).
425    ///
426    /// Note: This doesn't zero out element slots, just resets the length.
427    pub fn clear_in<B: StorageBackend>(&self, backend: &mut B) -> Result<()> {
428        backend.write_32(self.len_slot(), 0u64.encode_32())
429    }
430
431    /// Returns the number of elements (production, uses `HostStorage`).
432    pub fn len(&self) -> Result<u64> {
433        let host = HostStorage;
434        self.len_in(&host)
435    }
436
437    /// Gets the element at index (production, uses `HostStorage`).
438    pub fn get(&self, index: u64) -> Result<Option<V>> {
439        let host = HostStorage;
440        self.get_in(&host, index)
441    }
442
443    /// Sets the element at index (production, uses `HostStorage`).
444    pub fn set(&self, index: u64, value: &V) -> Result<bool> {
445        let mut host = HostStorage;
446        self.set_in(&mut host, index, value)
447    }
448
449    /// Appends an element to the end (production, uses `HostStorage`).
450    pub fn push(&self, value: &V) -> Result<u64> {
451        let mut host = HostStorage;
452        self.push_in(&mut host, value)
453    }
454
455    /// Removes and returns the last element (production, uses `HostStorage`).
456    pub fn pop(&self) -> Result<Option<V>> {
457        let mut host = HostStorage;
458        self.pop_in(&mut host)
459    }
460
461    /// Clears the vector (production, uses `HostStorage`).
462    pub fn clear(&self) -> Result<()> {
463        let mut host = HostStorage;
464        self.clear_in(&mut host)
465    }
466}
467
468/// A persistent variable-length byte storage.
469///
470/// `StorageBlob` stores arbitrary-length byte arrays by chunking them into
471/// 32-byte segments. Useful for storing strings, serialized structs, or large data.
472///
473/// # Storage Layout
474///
475/// - `derive_slot(namespace, ["blob:len"])` - Length in bytes as u64
476/// - `derive_slot(namespace, ["blob:chunk", i])` - Chunk i (32 bytes)
477///
478/// # Example
479///
480/// ```ignore
481/// const NS_METADATA: Namespace = Namespace([1u8; 32]);
482/// let metadata = StorageBlob::new(NS_METADATA);
483///
484/// // Write raw bytes
485/// metadata.write(b"Hello, TruthLinked!")?;
486///
487/// // Read raw bytes
488/// let data = metadata.read()?;
489///
490/// // Write typed value
491/// let config = Config { version: 1, enabled: true };
492/// metadata.put(&config)?;
493///
494/// // Read typed value
495/// let config: Config = metadata.get()?;
496/// ```
497pub struct StorageBlob {
498    namespace: Namespace,
499}
500
501impl StorageBlob {
502    /// Creates a new blob with the specified namespace.
503    pub const fn new(namespace: Namespace) -> Self {
504        Self { namespace }
505    }
506
507    /// Returns the storage slot for the length counter.
508    pub fn len_slot(&self) -> [u8; 32] {
509        hashing::derive_slot(&self.namespace.0, &[b"blob:len"])
510    }
511
512    /// Returns the storage slot for a chunk at the given index.
513    pub fn slot_for_chunk(&self, index: u64) -> [u8; 32] {
514        let idx = hashing::index_u64(index);
515        hashing::derive_slot(&self.namespace.0, &[b"blob:chunk", &idx])
516    }
517
518    /// Writes raw bytes to storage (with explicit backend).
519    ///
520    /// Data is chunked into 32-byte segments. The length is stored separately.
521    pub fn write_in<B: StorageBackend>(&self, backend: &mut B, data: &[u8]) -> Result<()> {
522        let chunks = if data.is_empty() {
523            0
524        } else {
525            ((data.len() - 1) / 32) + 1
526        };
527        for i in 0..chunks {
528            let start = i * 32;
529            let end = (start + 32).min(data.len());
530            let mut chunk = [0u8; 32];
531            chunk[..(end - start)].copy_from_slice(&data[start..end]);
532            backend.write_32(self.slot_for_chunk(i as u64), chunk)?;
533        }
534        backend.write_32(self.len_slot(), (data.len() as u64).encode_32())?;
535        Ok(())
536    }
537
538    /// Reads raw bytes from storage (with explicit backend).
539    ///
540    /// Reconstructs the original byte array from stored chunks.
541    pub fn read_in<B: StorageBackend>(&self, backend: &B) -> Result<Vec<u8>> {
542        let raw_len = backend.read_32(&self.len_slot())?;
543        let len = <u64 as Codec32>::decode_32(&raw_len)? as usize;
544        if len == 0 {
545            return Ok(Vec::new());
546        }
547        let chunks = ((len - 1) / 32) + 1;
548        let mut out = vec![0u8; chunks * 32];
549        for i in 0..chunks {
550            let chunk = backend.read_32(&self.slot_for_chunk(i as u64))?;
551            let start = i * 32;
552            out[start..start + 32].copy_from_slice(&chunk);
553        }
554        out.truncate(len);
555        Ok(out)
556    }
557
558    /// Clears the blob by setting length to 0 (with explicit backend).
559    pub fn clear_in<B: StorageBackend>(&self, backend: &mut B) -> Result<()> {
560        backend.write_32(self.len_slot(), 0u64.encode_32())
561    }
562
563    /// Writes a typed value to storage (with explicit backend).
564    ///
565    /// The value is encoded using `BytesCodec` before storage.
566    pub fn put_in<B: StorageBackend, T: BytesCodec>(
567        &self,
568        backend: &mut B,
569        value: &T,
570    ) -> Result<()> {
571        self.write_in(backend, &value.encode_bytes())
572    }
573
574    /// Reads a typed value from storage (with explicit backend).
575    ///
576    /// The bytes are decoded using `BytesCodec`.
577    pub fn get_in<B: StorageBackend, T: BytesCodec>(&self, backend: &B) -> Result<T> {
578        let raw = self.read_in(backend)?;
579        T::decode_bytes(&raw)
580    }
581
582    /// Writes raw bytes to storage (production, uses `HostStorage`).
583    pub fn write(&self, data: &[u8]) -> Result<()> {
584        let mut host = HostStorage;
585        self.write_in(&mut host, data)
586    }
587
588    /// Reads raw bytes from storage (production, uses `HostStorage`).
589    pub fn read(&self) -> Result<Vec<u8>> {
590        let host = HostStorage;
591        self.read_in(&host)
592    }
593
594    /// Writes a typed value to storage (production, uses `HostStorage`).
595    pub fn put<T: BytesCodec>(&self, value: &T) -> Result<()> {
596        let mut host = HostStorage;
597        self.put_in(&mut host, value)
598    }
599
600    /// Reads a typed value from storage (production, uses `HostStorage`).
601    pub fn get<T: BytesCodec>(&self) -> Result<T> {
602        let host = HostStorage;
603        self.get_in(&host)
604    }
605
606    /// Clears the blob (production, uses `HostStorage`).
607    pub fn clear(&self) -> Result<()> {
608        let mut host = HostStorage;
609        self.clear_in(&mut host)
610    }
611}
612
613#[cfg(test)]
614mod tests {
615    use super::*;
616    use crate::backend::MemoryStorage;
617
618    const NS_MAP: Namespace = Namespace([1u8; 32]);
619    const NS_VEC: Namespace = Namespace([2u8; 32]);
620    const NS_BLOB: Namespace = Namespace([3u8; 32]);
621
622    #[test]
623    fn map_roundtrip() {
624        let mut mem = MemoryStorage::new();
625        let map = StorageMap::<u64>::new(NS_MAP);
626
627        assert_eq!(map.get_in(&mem, b"alice").unwrap(), None);
628        map.insert_in(&mut mem, b"alice", &42).unwrap();
629        assert_eq!(map.get_in(&mem, b"alice").unwrap(), Some(42));
630        map.remove_in(&mut mem, b"alice").unwrap();
631        assert_eq!(map.get_in(&mem, b"alice").unwrap(), None);
632    }
633
634    #[test]
635    fn vec_roundtrip() {
636        let mut mem = MemoryStorage::new();
637        let list = StorageVec::<u64>::new(NS_VEC);
638
639        assert_eq!(list.len_in(&mem).unwrap(), 0);
640        list.push_in(&mut mem, &7).unwrap();
641        list.push_in(&mut mem, &9).unwrap();
642        assert_eq!(list.len_in(&mem).unwrap(), 2);
643        assert_eq!(list.get_in(&mem, 1).unwrap(), Some(9));
644        assert_eq!(list.pop_in(&mut mem).unwrap(), Some(9));
645        assert_eq!(list.len_in(&mem).unwrap(), 1);
646    }
647
648    #[test]
649    fn blob_roundtrip() {
650        let mut mem = MemoryStorage::new();
651        let blob = StorageBlob::new(NS_BLOB);
652        let data = b"hello storage blob".to_vec();
653
654        blob.write_in(&mut mem, &data).unwrap();
655        assert_eq!(blob.read_in(&mem).unwrap(), data);
656    }
657
658    #[test]
659    fn map_typed_keys_roundtrip() {
660        let mut mem = MemoryStorage::new();
661        let map = StorageMap::<u64>::new(NS_MAP);
662        let key = [7u8; 32];
663
664        map.insert_typed_key_in(&mut mem, &key, &88).unwrap();
665        assert_eq!(map.get_typed_key_in(&mem, &key).unwrap(), Some(88));
666        map.remove_typed_key_in(&mut mem, &key).unwrap();
667        assert_eq!(map.get_typed_key_in(&mem, &key).unwrap(), None);
668    }
669}