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}