Usage Guide and Examples
Relevant source files
This document provides practical guidance for using the FlattenObjects
container in real-world applications. It covers common usage patterns, best practices, and integration strategies for kernel-level and embedded systems development.
For detailed API documentation, see FlattenObjects API Documentation. For implementation details and internal architecture, see Implementation Details.
Container Creation and Basic Operations
The FlattenObjects
container requires compile-time specification of both the object type and maximum capacity. This design ensures memory efficiency and predictable behavior in resource-constrained environments.
Basic Container Workflow
flowchart TD subgraph RemoveOps["Remove Operations"] Remove["remove(id)"] RemoveResult["Returns Option"] MaybeEmpty["Container may become empty"] end subgraph AddOps["Object Addition Operations"] Add["add(object)"] FindID["Find next available ID"] AddAt["add_at(id, object)"] CheckID["Check if ID available"] AddReplace["add_or_replace_at(id, object)"] ForceID["Use specified ID"] Success1["Returns assigned ID"] Success2["Returns () on success"] Success3["Returns previous object if any"] end subgraph QueryOps["Query Operations"] Get["get(id)"] GetResult["Returns Option<&T>"] GetMut["get_mut(id)"] GetMutResult["Returns Option<&mut T>"] IsAssigned["is_assigned(id)"] BoolResult["Returns bool"] Create["FlattenObjects::new()"] Empty["Empty Containercount = 0"] end Active["Active Containercount > 0"] Add --> FindID AddAt --> CheckID AddReplace --> ForceID CheckID --> Success2 Create --> Empty FindID --> Success1 ForceID --> Success3 Get --> GetResult GetMut --> GetMutResult IsAssigned --> BoolResult MaybeEmpty --> Empty Remove --> RemoveResult RemoveResult --> MaybeEmpty Success1 --> Active Success2 --> Active Success3 --> Active
Basic Usage Example from README
The fundamental usage pattern demonstrates object insertion, retrieval, and removal:
This example shows:
- Container creation with
FlattenObjects::<u32, 20>::new()
- Sequential ID assignment using
add_at()
- Object removal with
remove()
- Automatic ID reuse when using
add()
- State checking with
is_assigned()
Sources: README.md(L14 - L36)
Object Lifecycle Management
Sources: README.md(L19 - L35)
ID Management Patterns
The container provides flexible ID assignment strategies for different use cases:
Automatic ID Assignment
// From README example - automatic ID assignment
let id = objects.add(42).unwrap();
// ID 6 was reused from previously removed object
assert_eq!(id, 6);
This pattern uses add()
to let the container find the next available ID, prioritizing ID reuse for memory efficiency.
Explicit ID Assignment
// From README example - explicit ID assignment
for i in 0..=9 {
objects.add_at(i, 23).unwrap();
}
This pattern uses add_at()
when specific ID values are required, such as for handle tables or slot-based allocation systems.
ID Assignment Strategy Comparison
Method | Use Case | ID Selection | Error on Conflict |
---|---|---|---|
add() | General object pooling | Automatic (reuses lowest) | No (finds available) |
add_at() | Handle tables, specific slots | Explicit | Yes (fails if occupied) |
add_or_replace_at() | Overwrite semantics | Explicit | No (replaces existing) |
Sources: README.md(L19 - L35)
Error Handling Strategies
The container uses Result
types for operations that can fail, enabling robust error handling in system code:
Common Error Scenarios
flowchart TD subgraph subGraph2["Return Types"] ResultID["Result"] ResultUnit["Result<(), ()>"] OptionRef["Option<&T>"] OptionOwned["Option"] end subgraph subGraph1["Error Conditions"] CapFull["Container at capacity(count == CAP)"] IDTaken["ID already assigned"] IDInvalid["ID not assigned"] end subgraph subGraph0["Operation Types"] AddOp["add() operation"] AddAtOp["add_at() operation"] GetOp["get() / get_mut() operation"] RemoveOp["remove() operation"] end AddAtOp --> CapFull AddAtOp --> IDTaken AddAtOp --> ResultUnit AddOp --> CapFull AddOp --> ResultID CapFull --> ResultID CapFull --> ResultUnit GetOp --> IDInvalid GetOp --> OptionRef IDInvalid --> OptionOwned IDInvalid --> OptionRef IDTaken --> ResultUnit RemoveOp --> IDInvalid RemoveOp --> OptionOwned
Error Handling Pattern Example
// Based on README pattern - defensive programming
match objects.add(value) {
Ok(id) => {
// Successfully added, use the returned ID
println!("Object assigned ID: {}", id);
}
Err(()) => {
// Container is full, handle gracefully
eprintln!("Container capacity exceeded");
}
}
Sources: README.md(L30 - L31)
Integration with System Components
The FlattenObjects
container is designed for integration into kernel and embedded system components:
Typical Integration Patterns
flowchart TD subgraph subGraph3["Common Operations"] Alloc["Handle Allocationadd() / add_at()"] Lookup["Handle Resolutionget() / get_mut()"] Release["Resource Cleanupremove()"] end subgraph subGraph2["Object Types"] Proc["ProcessDescriptor"] File["FileHandle"] Conn["TcpConnection"] Dev["DeviceHandle"] end subgraph subGraph1["FlattenObjects Usage"] ProcTable["Process Handle TableFlattenObjects"] FileTable["File Descriptor TableFlattenObjects"] ConnTable["Connection PoolFlattenObjects"] DevTable["Device RegistryFlattenObjects"] end subgraph subGraph0["System Layer"] Kernel["Kernel Subsystem"] Driver["Device Driver"] FS["File System"] Net["Network Stack"] end ConnTable --> Conn ConnTable --> Release DevTable --> Alloc DevTable --> Dev Driver --> DevTable FS --> FileTable FileTable --> File FileTable --> Lookup Kernel --> ProcTable Net --> ConnTable ProcTable --> Alloc ProcTable --> Proc
Resource Management Pattern
#![allow(unused)] fn main() { // Typical kernel subsystem integration pattern struct ProcessManager { processes: FlattenObjects<ProcessDescriptor, MAX_PROCESSES>, } impl ProcessManager { pub fn spawn_process(&mut self, descriptor: ProcessDescriptor) -> Result<ProcessId, SpawnError> { match self.processes.add(descriptor) { Ok(pid) => Ok(ProcessId(pid)), Err(()) => Err(SpawnError::TooManyProcesses), } } pub fn get_process(&self, pid: ProcessId) -> Option<&ProcessDescriptor> { self.processes.get(pid.0) } pub fn terminate_process(&mut self, pid: ProcessId) -> Option<ProcessDescriptor> { self.processes.remove(pid.0) } } }
This pattern demonstrates:
- Wrapping
FlattenObjects
in higher-level abstractions - Converting container IDs to domain-specific handle types
- Providing semantic error types for integration
Sources: README.md(L7 - L11)
Container State Inspection
The container provides methods for monitoring and debugging:
State Inspection Methods
Method | Return Type | Purpose |
---|---|---|
capacity() | usize | Maximum number of objects |
count() | usize | Current number of objects |
is_assigned(id) | bool | Check if specific ID is in use |
ids() | Iterator | Iterate over assigned IDs |
Monitoring Pattern
// Based on available methods from API
fn print_container_status<T, const CAP: usize>(container: &FlattenObjects<T, CAP>) {
println!("Capacity: {}/{}", container.count(), container.capacity());
println!("Assigned IDs: {:?}", container.ids().collect::<Vec<_>>());
// Check specific ID ranges
for id in 0..container.capacity() {
if container.is_assigned(id) {
println!("ID {} is assigned", id);
}
}
}
This monitoring approach is essential for:
- Resource usage tracking in kernel subsystems
- Debugging handle leaks
- Capacity planning for system limits
Sources: README.md(L22 - L32)