service-builder (WIP)
Not building yet
A lightweight, type-safe service construction library for Rust that leverages the builder pattern to provide a more idiomatic alternative to traditional dependency injection.
Why Builder Pattern in Rust?
1. Ownership and Borrowing
Traditional dependency injection frameworks often struggle with Rust's ownership system. The builder pattern works naturally with Rust's ownership rules:
// ❌ Traditional DI approach - fights with ownership
container.;
let service = container..unwrap; // Runtime checks
// ✅ Builder pattern - works with ownership
let service = builder
.repository
.cache
.build?; // Compile-time checks
2. Compile-Time Guarantees
Rust's type system can catch dependency issues at compile time with the builder pattern:
// Won't compile if you forget a dependency
let service = builder
.repository
// Forgot .cache()
.build; // Compile error!
3. Clear Dependency Flow
Dependencies are explicit and visible in the code:
let auth_service = builder
.user_repository
.token_service
.build?;
let post_service = builder
.post_repository
.auth_service // Clear dependency chain
.build?;
Quick Start
Add this to your Cargo.toml:
[]
= "0.1.0"
Basic usage:
use *;
use Arc;
// In your main.rs or setup code
let user_service = builder
.repository
.cache
.build?;
let app_services = builder
.user_service
.post_service
.build?;
Builder Pattern vs Traditional DI
Memory Safety and Ownership
// ❌ DI Container - Potential runtime panics
let service = container..unwrap;
// ✅ Builder Pattern - Ownership is clear and enforced
let service = builder
.dependency
.build?;
Type Safety
// ❌ DI Container - Runtime type checks
container.;
// ✅ Builder Pattern - Compile-time type checks
Testing
// ✅ Easy mock injection with builder
Advanced Features
Error Handling
// Usage
let result = builder
.dependency
.build
.map_err?;
Async Initialization
Best Practices
- Use Arc for Shared Services
- Group Related Services
- Clear Error Handling
let service = builder
.dependency
.build
.expect;
Performance Considerations
The builder pattern in Rust has several performance advantages:
- Zero runtime overhead for dependency resolution
- No reflection or dynamic dispatch (unless explicitly used with trait objects)
- Smaller binary size compared to DI frameworks
- Better optimization opportunities for the compiler
Why Not Traditional DI?
- Runtime Overhead: Traditional DI containers need to resolve dependencies at runtime
- Type Erasure: Many DI solutions rely heavily on type erasure and runtime checks
- Ownership Complexity: DI frameworks often struggle with Rust's ownership rules
- Hidden Dependencies: Dependencies are often hidden in container configuration
- Runtime Failures: Many dependency issues only surface at runtime
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
All commit messages generated by opencommit