Resilient Standard Library Portability Model
Overview
Resilient’s standard library is organized into portability tiers that enable code to be written once and deployed across desktop, embedded, and constrained environments. This document specifies the tiers, their guarantees, and the rules for using and extending them.
Portability Tiers
Tier 0: Core (Universal)
Available: Everywhere (std, no_std, embedded, WASM)
No dependencies: Only language primitives, no external crates
APIs:
- Basic types: int, float, bool, string, arrays, structs
- Pure functions: arithmetic, logic, string manipulation
- Stack allocation and static allocation
- Pattern matching and control flow
- Function pointers and higher-order functions
Example:
pub fn add(int x, int y) -> int {
return x + y;
}
pub fn reverse<T>(items: &array<T>) -> array<T> {
let mut result: array<T>(items.len());
for i in 0..items.len() {
result[items.len() - 1 - i] = items[i];
}
return result;
}
Guarantees:
- Zero allocator dependency
- Runs on all targets without features
- Same performance on all backends
- No platform-specific code
Tier 1: Alloc (Heap Support)
Available: Any target with allocator (#[cfg(feature = "alloc")])
Dependency: Allocator (global or injected)
APIs:
- Dynamic arrays (Vec-like containers)
- String construction and mutation
- Hash tables / dictionaries
- Linked structures
- Reference counting (Rc, Arc)
Example:
#[cfg(feature = "alloc")]
pub fn collect_results<T>(items: &array<T>) -> Vec<T> {
let mut results = Vec::new();
for item in items {
results.push(item);
}
return results;
}
Constraints:
- Only available when compiled with
allocfeature - May fail if allocator runs out of memory
- Allocation is not deterministic (timing varies)
- Not suitable for hard real-time systems
Guarantees:
- Memory safety (no leaks, no double-free)
- Thread-safety (with proper synchronization)
- API stable across all allocator implementations
Tier 2: Std (Host Only)
Available: Linux, Windows, macOS, WASM only
Dependency: Standard library
APIs:
- File I/O
- Environment access
- Process control
- Standard input/output
- Networking (future)
- Time and timers
- Threading (future)
Example:
#[cfg(feature = "std")]
pub fn read_config(path: string) -> Result<string, string> {
// use std file I/O
}
#[cfg(not(feature = "std"))]
pub fn read_config(path: string) -> Result<string, string> {
return Err("file I/O not available in embedded mode");
}
Constraints:
- Only available on host targets
- Requires OS support (not suitable for bare metal)
- May depend on external system libraries
- Platform-specific behavior
Guarantees:
- Same API across all host platforms (with platform-specific details documented)
- Access to system resources
- Integration with OS facilities
Tier 3: Platform-Specific
Available: Only on specific platforms
Dependency: Platform-specific system libraries
APIs:
- MMIO registers (embedded)
- Interrupt handlers (embedded)
- System calls (Unix/Windows)
- GPU operations (future)
- Custom hardware (platform-dependent)
Example:
#[cfg(target = "thumbv7em-none-eabihf")]
pub mod cortex_m {
pub fn enable_interrupts() {
// Cortex-M specific
}
}
#[cfg(target = "riscv32imac-unknown-none-elf")]
pub mod risc_v {
pub fn enable_interrupts() {
// RISC-V specific
}
}
Constraints:
- Only usable on the target platform
- API may differ significantly between platforms
- Implementation is platform-dependent
Feature Flags for Portability
Standard Feature Set
[features]
default = ["std", "alloc"]
std = []
alloc = []
core = [] # core-only (explicit opt-out)
Feature Combinations
| Features | Tier | Use Case |
|---|---|---|
| (none) | Core | Bare metal, extreme constraints |
| alloc | Alloc | Embedded with heap |
| std | Std | Desktop/server |
| std + alloc | Std + Alloc | Full platform |
Example dependency with features:
[dependencies]
my_lib = { version = "1.0" } # gets default (std + alloc)
my_lib = { version = "1.0", default-features = false } # core only
my_lib = { version = "1.0", default-features = false, features = ["alloc"] } # alloc only
Writing Portable Code
Pattern 1: Conditional Compilation with Fallbacks
#[cfg(feature = "std")]
pub fn read_file(path: string) -> Result<string, string> {
// Use std file I/O
}
#[cfg(not(feature = "std"))]
pub fn read_file(path: string) -> Result<string, string> {
Err("file I/O not available in no_std".to_string())
}
Pattern 2: Tier-Based Abstractions
// Tier 0: Core
pub fn hash(data: &array<byte>) -> int {
// Pure computation, no allocation
let mut h = 0;
for b in data {
h = h ^ (b as int);
}
return h;
}
// Tier 1: Alloc
#[cfg(feature = "alloc")]
pub fn hash_vec(data: &Vec<byte>) -> int {
let slice = data.as_slice(); // Core function
return hash(slice);
}
// Tier 2: Std
#[cfg(feature = "std")]
pub fn hash_file(path: string) -> Result<int, string> {
let data = try read_file(path); // from Tier 2
return Ok(hash(data.as_bytes())); // use Core
}
Pattern 3: Optional Optimization with Features
pub fn process(data: &array<int>) -> int {
#[cfg(feature = "fast_path")]
{
return process_optimized(data);
}
#[cfg(not(feature = "fast_path"))]
{
return process_generic(data);
}
}
Allocator Abstractions
Global Allocator
#[cfg(feature = "alloc")]
extern "C" {
fn malloc(size: int) -> &mut byte;
fn free(ptr: &mut byte);
}
#[cfg(feature = "alloc")]
pub struct Allocator {
// global allocator instance
}
#[cfg(feature = "alloc")]
impl Allocator {
pub fn alloc(size: int) -> Option<&mut byte> {
// platform-specific
}
}
Injected Allocator
pub struct Config<A: Allocator> {
allocator: A,
}
pub fn with_allocator<A: Allocator>(alloc: A) -> Config<A> {
Config { allocator: alloc }
}
Embedded Portability
Constraint Profile
#[cfg(target = "embedded")]
pub mod constraints {
// Typical embedded constraints:
// - No dynamic allocation (or very limited)
// - No concurrency (single-threaded)
// - Deterministic timing required
// - Memory budget: 256 KB - 2 MB
// - No OS (bare metal)
}
Embedded-Friendly APIs
#[cfg(all(feature = "alloc", target = "embedded"))]
compile_error!("heap allocation not available in embedded mode");
pub fn embedded_safe_process(input: &array<int>) -> Result<int, string> {
// Stack-only, no allocation
let mut result = 0;
for v in input {
result += v;
}
return Ok(result);
}
Testing Across Tiers
Test Organization
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_core_function() {
// runs everywhere
}
#[test]
#[cfg(feature = "alloc")]
fn test_alloc_function() {
// runs only with alloc
}
#[test]
#[cfg(feature = "std")]
fn test_std_function() {
// runs only on host
}
}
Continuous Integration Matrix
test_matrix:
- feature_set: "core" # no features
targets: ["x86_64", "cortex-m4", "riscv32"]
- feature_set: "alloc" # alloc only
targets: ["x86_64", "cortex-m4"]
- feature_set: "std" # std only
targets: ["x86_64"]
- feature_set: "std+alloc" # full
targets: ["x86_64"]
Documentation Requirements
For Public APIs
Example:
/// Computes sum of integers in array.
///
/// # Availability
/// - Tier 0 (Core): Always available
///
/// # Example
/// ```
/// let arr = [1, 2, 3];
/// assert_eq!(sum(&arr), 6);
/// ```
pub fn sum(items: &array<int>) -> int {
let mut total = 0;
for item in items {
total += item;
}
return total;
}
/// Reads file into string.
///
/// # Availability
/// - Tier 2 (Std): Requires `std` feature
///
/// # Errors
/// - File not found
/// - Permission denied
/// - I/O error
///
/// # Example
/// ```ignore
/// let content = read_file("config.txt")?;
/// ```
#[cfg(feature = "std")]
pub fn read_file(path: string) -> Result<string, string> {
// ...
}
Migration Path: Tier-Locked Code to Portable
Step 1: Identify tier-specific calls
// Before: depends on std
pub fn process(path: string) -> int {
let data = read_file(path); // std only
return compute(data);
}
Step 2: Extract core logic
pub fn compute(data: string) -> int {
// Tier 0: Core logic, no I/O
let mut count = 0;
for ch in data {
count += 1;
}
return count;
}
Step 3: Make I/O conditional
#[cfg(feature = "std")]
pub fn process_file(path: string) -> Result<int, string> {
let data = try read_file(path);
return Ok(compute(data)); // use core function
}
pub fn process_data(data: string) -> int {
return compute(data); // works everywhere
}
Best Practices
1. Write Core First, Extend Later
// Good: core function first
pub fn find_max(items: &array<int>) -> int {
let mut max = items[0];
for item in items {
if item > max { max = item; }
}
return max;
}
// Extended: higher-tier wrapper
#[cfg(feature = "alloc")]
pub fn find_max_vec(items: &Vec<int>) -> int {
find_max(items.as_slice())
}
2. Document Tier Requirements
/// Core algorithm: O(n) sum.
/// Availability: Tier 0 (Core)
pub fn sum_core(items: &array<int>) -> int { ... }
/// Heap-backed result collection.
/// Availability: Tier 1 (Alloc)
#[cfg(feature = "alloc")]
pub fn collect_all(items: &array<int>) -> Vec<int> { ... }
3. Test Each Tier
- Core: Test without features
- Alloc: Test with
alloconly - Std: Test with
stdonly - Full: Test with both
4. Minimize Feature Usage
Fewer features = more portable code.
// Prefer: fewer dependencies
pub fn swap<T>(a: &mut T, b: &mut T) {
// no features needed
}
// Avoid: gratuitous features
pub fn swap_with_logging<T>(a: &mut T, b: &mut T) {
#[cfg(feature = "std")]
eprintln!("swapping");
}
Roadmap
v0.4: Tier Definition and Tooling
- Feature flag conventions
- Portability checking in compiler
- CI matrix generation
v0.5: Standard Library Expansion
- Complete Tier 1 (Alloc) APIs
- Stable Tier 2 (Std) APIs
- Allocator traits and implementations
v0.6+: Advanced Portability
- WASM tier support
- Async portability (Future traits)
- No_std error handling improvements
- Embedded-specific tooling (linker scripts, memory layouts)
Summary Table
| Tier | Availability | Allocation | I/O | Concurrency | Use Case |
|---|---|---|---|---|---|
| 0: Core | Universal | Stack/static | No | No | All platforms |
| 1: Alloc | With feature | Heap | No | No | Embedded + host |
| 2: Std | Host only | Heap | Yes | No (future) | Desktop/server |
| 3: Platform | Platform-specific | Platform | Yes | Platform | Native code |
References
- RES-3507: Design a production-grade standard library portability model
- RES-3502: Module and package system design
- MEMORY_MODEL.md: Allocation tiers and constraints
- MODULE_SYSTEM.md: Package feature flags