pub struct AddressToIdMapper<SA, A = CurrentStorage>where
SA: StorageMapperApi,
A: StorageAddress<SA>,{ /* private fields */ }Expand description
A specialized bidirectional mapper between addresses and auto-incrementing numeric IDs.
§Storage Layout
The AddressToIdMapper maintains bidirectional mappings with sequential ID assignment:
-
Address to ID mapping:
base_key + "addr" + encoded_address→ assigned ID (u64)
-
ID to address mapping:
base_key + "addrId" + id→ address
-
ID counter:
base_key + "lastId"→ highest assigned ID (for generating new IDs)
§ID Assignment
- IDs start from 1 and increment sequentially
NULL_ID(0) represents “no ID assigned” or “not found”- Once an ID is assigned, it remains associated with that address until explicitly removed
- Removed IDs are not reused (IDs only increment, never decrement)
§Main Operations
- Auto-insert:
get_id_or_insert(address)- Gets ID or assigns new one if not exists. O(1). - Strict insert:
insert_new(address)- Assigns ID only if address is new, errors if exists. O(1). - Lookup ID:
get_id(address)- Returns ID for address (0 if not found). O(1). - Lookup Address:
get_address(id)- Returns address for ID. O(1). - Contains:
contains_id(id)- Checks if ID is assigned. O(1). - Remove by ID:
remove_by_id(id)- Removes mapping by ID, returns address. O(1). - Remove by Address:
remove_by_address(address)- Removes mapping by address, returns ID. O(1). - Counter:
get_last_id()- Returns the highest assigned ID.
§Trade-offs
- Pros: Sequential IDs are compact and predictable; auto-increment simplifies ID management; O(1) bidirectional lookup; no duplicate addresses.
- Cons: IDs are never reused (gaps after removal); ID overflow at u64::MAX (unlikely but possible);
slightly less flexible than generic
BiDiMapper.
§Comparison with BiDiMapper
- AddressToIdMapper: Specialized for addresses; auto-incrementing IDs; single type for IDs (u64)
- BiDiMapper: Generic; manual ID/value assignment; supports any types for both sides
§Use Cases
- User registration systems (address → user ID)
- Whitelist/participant management with sequential numbering
- Address indexing for efficient iteration
- Mapping external addresses to internal compact IDs
- Any scenario where addresses need numeric identifiers
§Example
// Auto-assign IDs (get or create)
let id1 = mapper.get_id_or_insert(&addr1); // Returns 1
let id2 = mapper.get_id_or_insert(&addr2); // Returns 2
let id1_again = mapper.get_id_or_insert(&addr1); // Returns 1 (existing)
assert_eq!(id1, 1);
assert_eq!(id2, 2);
assert_eq!(id1_again, 1);
assert_eq!(mapper.get_last_id(), 2);
// Strict insert (errors if address already exists)
let id3 = mapper.insert_new(&addr3); // Returns 3
assert_eq!(id3, 3);
// mapper.insert_new(&addr1); // Would error: "Address already registered"
// Bidirectional lookup
assert_eq!(mapper.get_id(&addr1), 1);
assert_eq!(mapper.get_address(1), Some(addr1.clone()));
// Check existence
assert!(mapper.contains_id(2));
assert_eq!(mapper.get_id(&addr2), 2); // Returns 2
// Non-zero lookup (errors if not found)
let id = mapper.get_id_non_zero(&addr1); // Returns 1
assert_eq!(id, 1);
// mapper.get_id_non_zero(&unknown_addr); // Would error: "Unknown address"
// Remove by address
let removed_id = mapper.remove_by_address(&addr2);
assert_eq!(removed_id, 2);
assert_eq!(mapper.get_id(&addr2), 0); // Now returns NULL_ID
assert!(!mapper.contains_id(2));
// Remove by ID
let removed_addr = mapper.remove_by_id(1);
assert_eq!(removed_addr, Some(addr1.clone()));
assert_eq!(mapper.get_id(&addr1), 0);
// Note: next inserted address gets ID 4, not 2 (IDs never reused)Implementations§
Source§impl<SA, A> AddressToIdMapper<SA, A>where
SA: StorageMapperApi,
A: StorageAddress<SA>,
impl<SA, A> AddressToIdMapper<SA, A>where
SA: StorageMapperApi,
A: StorageAddress<SA>,
pub fn contains_id(&self, id: AddressId) -> bool
pub fn get_id(&self, address: &ManagedAddress<SA>) -> AddressId
pub fn get_id_non_zero(&self, address: &ManagedAddress<SA>) -> AddressId
pub fn get_address(&self, id: AddressId) -> Option<ManagedAddress<SA>>
pub fn get_last_id(&self) -> AddressId
Source§impl<SA> AddressToIdMapper<SA, CurrentStorage>where
SA: StorageMapperApi,
impl<SA> AddressToIdMapper<SA, CurrentStorage>where
SA: StorageMapperApi,
pub fn get_id_or_insert(&self, address: &ManagedAddress<SA>) -> AddressId
pub fn insert_new(&self, address: &ManagedAddress<SA>) -> AddressId
pub fn remove_by_id(&self, id: AddressId) -> Option<ManagedAddress<SA>>
pub fn remove_by_address(&self, address: &ManagedAddress<SA>) -> AddressId
Trait Implementations§
Source§impl<SA> StorageMapper<SA> for AddressToIdMapper<SA>where
SA: StorageMapperApi,
impl<SA> StorageMapper<SA> for AddressToIdMapper<SA>where
SA: StorageMapperApi,
Source§fn new(base_key: StorageKey<SA>) -> Self
fn new(base_key: StorageKey<SA>) -> Self
Will be called automatically by the
#[storage_mapper] annotation generated code.Source§impl<SA> StorageMapperFromAddress<SA> for AddressToIdMapper<SA, ManagedAddress<SA>>where
SA: StorageMapperApi,
impl<SA> StorageMapperFromAddress<SA> for AddressToIdMapper<SA, ManagedAddress<SA>>where
SA: StorageMapperApi,
Source§fn new_from_address(
address: ManagedAddress<SA>,
base_key: StorageKey<SA>,
) -> Self
fn new_from_address( address: ManagedAddress<SA>, base_key: StorageKey<SA>, ) -> Self
Will be called automatically by the
#[storage_mapper_from_address]
annotation generated code.Auto Trait Implementations§
impl<SA, A> Freeze for AddressToIdMapper<SA, A>
impl<SA, A> RefUnwindSafe for AddressToIdMapper<SA, A>where
A: RefUnwindSafe,
SA: RefUnwindSafe,
<SA as HandleTypeInfo>::ManagedBufferHandle: RefUnwindSafe,
impl<SA, A> Send for AddressToIdMapper<SA, A>
impl<SA, A> Sync for AddressToIdMapper<SA, A>
impl<SA, A> Unpin for AddressToIdMapper<SA, A>
impl<SA, A> UnsafeUnpin for AddressToIdMapper<SA, A>
impl<SA, A> UnwindSafe for AddressToIdMapper<SA, A>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more