elemental-object
A type-safe, thread-safe global object pool for Rust applications.
Features
- Type-Safe: Leverages Rust's type system to uniquely identify and retrieve objects
- Lock-Free: Built on
scc::HashMapfor efficient concurrent access - Async-Friendly: Full support for async/await patterns
- Lifecycle Management: Custom shutdown handlers for graceful cleanup
- Wait-for-Initialization: Optional notification system for coordinating startup
- Zero-Cost Abstractions: LazyLock ensures minimal overhead
Quick Start
Add to your Cargo.toml:
[]
= "0.1"
Basic usage:
use ;
async
Common Patterns
1. Global Singleton Service
Store and access application services without passing them through every function:
use ;
async
2. Wait-for-Initialization Pattern
Coordinate startup between multiple tasks:
use ;
async
3. Multiple Types in One Pool
Store different types without creating separate managers:
use ;
provide.await;
provide.await;
provide.await;
// Retrieve each by type
let db = ?;
let cache = ?;
let logger = ?;
4. Lifecycle with Cleanup
Ensure resources are properly cleaned up when replaced or removed:
use ;
// Provide resource with shutdown handler
provide.await;
// Later: explicitly remove (triggers cleanup)
.await;
// Or replace with new value (triggers cleanup of old value)
provide.await;
API Overview
Retrieving Values
| Function | Mode | Behavior |
|---|---|---|
require::<T>() |
Async | Get value or return error |
require_sync::<T>() |
Sync | Get value synchronously or return error |
acquire::<T>() |
Async | Wait for value to be provided, then return it |
Providing Values
| Function | Behavior |
|---|---|
provide::<T>(value, shutdown) |
Store owned value with optional shutdown handler |
provide_arc::<T>(arc_value, shutdown) |
Store Arc-wrapped value (avoids re-wrapping) |
fulfill::<T>(value) |
Mark value as fulfilled and notify waiters (idempotent) |
fulfill_arc::<T>(arc_value) |
Fulfill with Arc value |
Cleanup
| Function | Behavior |
|---|---|
drop_value::<T>() |
Remove value and call shutdown handler |
drop_entry::<T>() |
Remove entire entry and call shutdown handler |
shutdown() |
Gracefully shutdown all objects in pool |
Features
Default (with notification)
The default configuration includes the "notify" feature, enabling wait-for-initialization patterns:
use acquire;
let config = ?; // Available
Minimal (without notification)
For applications that don't need async waiting, reduce dependencies:
[]
= { = "0.1", = false }
This removes the Tokio dependency and disables acquire(), fulfill(), and wait_value().
Thread Safety
- All operations are thread-safe and concurrent
- Values are wrapped in
Arc<T>for shared ownership - Built on lock-free
scc::HashMapfor efficient concurrent access - Supports both sync and async access patterns
Type Requirements
Values stored in the pool must satisfy:
T: Any + Send + Sync
This includes:
- Most standard types (
String, integers, collections) - Custom structs and enums
- Trait objects (less common, as type identification becomes ambiguous)
Error Handling
When a value is not found in the pool:
use require;
match
The error message includes the type name for debugging:
Cannot get object `my_app::config::Config` from pool
Examples
See the tests for more complete examples:
test_fulfill: Wait-for-initialization patterntest_provide: Multiple types in pool
Run with:
Performance
- Retrieval: O(1) hash lookup
- Insertion/Update: O(1) amortized
- Shutdown: O(n) where n = number of types in pool
- Memory: Minimal overhead (LazyLock initializes on first use)
Design Document
For detailed architectural information, see DESIGN.md.
Integration with Elemental
This crate is part of the Elemental framework and is commonly used in conjunction with:
elemental-core: Core framework utilitieselemental-shared: Shared configuration and persistence
License
Licensed under the same terms as the Elemental project. See LICENSE for details.
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
For questions or issues, please open an issue on the GitHub repository.