LightClone
Compile-time enforcement for O(1) clone operations in Rust.
Overview
LightClone is for codebases that embrace immutable data structures. It provides a marker trait and derive macro that guarantees cloning is cheap by only allowing types where cloning involves:
- Atomic refcount increments (
Arc) - Non-atomic refcount increments (
Rc) - Bitwise copies (
Copytypes) - Persistent data structures (im, imbl, rpds with structural sharing)
Types like String, Vec, or HashMap that perform deep copies are rejected at compile time.
Usage
use LightClone;
use Arc;
let config = Config ;
// .light_clone() or .lc() for short
let clone = config.lc;
Compile-Time Safety
Add #[derive(Clone, LightClone)] to your structs and enums. All fields must implement LightClone:
use LightClone;
use Arc;
If any field doesn't implement LightClone, you get a compile error:
This ensures your types remain O(1) to clone as they evolve.
Ergonomic Strings
Use LightStr as a cheap-to-clone string type:
use ;
let s: LightStr = "hello".into_light_str;
let clone = s.lc; // O(1) - just increments refcount
Supported Types
Primitives
All primitive types: i8-i128, u8-u128, f32, f64, bool, char, ()
Smart Pointers
Arc<T>whereT: ?SizedRc<T>whereT: ?Sizedstd::sync::Weak<T>whereT: ?Sizedstd::rc::Weak<T>whereT: ?Sized
Containers
Option<T>whereT: LightCloneResult<T, E>whereT: LightClone, E: LightClonePhantomData<T>- Tuples up to 12 elements
[T; N]arrays whereT: LightClone + Copy
Wrapper Types
Pin<T>whereT: LightCloneBound<T>whereT: LightClonePoll<T>whereT: LightCloneManuallyDrop<T>whereT: LightCloneCell<T>whereT: LightClone + CopyNonNull<T>whereT: ?Sized
Function Pointers
fn(...) -> Rwith up to 12 arguments
Enums
Implementing for Custom Types
Since LightClone is a marker trait, you can implement it for your own types or third-party types that are O(1) to clone:
use LightClone;
use Arc;
// For a type you know is O(1) to clone
;
The trait provides default implementations for light_clone() and lc() that delegate to clone(), so an empty impl is all you need.
This is useful for:
- Third-party types that are O(1) to clone but don't have built-in LightClone support
- Newtypes wrapping LightClone types
- Types from external crates where you can't use the derive macro
Features
Enable integrations with popular crates via feature flags:
[]
= { = "0.3", = ["bytes", "smol_str"] }
Persistent Collections
| Feature | Crate | Types |
|---|---|---|
im |
im | Vector, HashMap, HashSet, OrdMap, OrdSet |
imbl |
imbl | Vector, HashMap, HashSet, OrdMap, OrdSet |
rpds |
rpds | Vector, List, Queue, Stack, HashTrieMap, HashTrieSet, RedBlackTreeMap, RedBlackTreeSet |
Common Types
| Feature | Crate | Types | Clone Mechanism |
|---|---|---|---|
bytes |
bytes | Bytes |
Arc-based ref counting |
smol_str |
smol_str | SmolStr |
Inline or Arc |
uuid |
uuid | Uuid |
Copy (128-bit) |
rust_decimal |
rust_decimal | Decimal |
Copy (128-bit) |
ordered-float |
ordered-float | OrderedFloat<T>, NotNan<T> |
Copy wrapper |
chrono |
chrono | NaiveDate, NaiveTime, NaiveDateTime, DateTime<Tz>, Month, Weekday, TimeDelta, Utc, FixedOffset |
Copy |
time |
time | Date, Time, PrimitiveDateTime, OffsetDateTime, UtcOffset, Duration, Month, Weekday |
Copy |
Meta Features
| Feature | Description |
|---|---|
full |
Enable all optional integrations |
When to Use Immutable Data Structures
LightClone enforces that your types use immutable data structures (Arc, Rc, persistent collections) which enable O(1) cloning through structural sharing. This approach shines when:
- Clone-heavy workloads - Sharing state across threads, event sourcing, undo/redo systems, functional pipelines with pure transforms
- Cloning large or nested data - A 10KB string clone copies 10KB; an
Arc<str>clone increments a counter - Concurrent code - Clone and send freely without worrying about data races or locks
- Structural sharing matters - Persistent collections share unchanged portions between versions
The trade-offs to consider:
- Mutation is still faster than cloning - LightClone enforces cloning is cheap, not free. In-place mutation avoids refcount operations entirely, so prefer mutation for hot paths
- Memory overhead - Arc/Rc add pointer indirection and allocation overhead. Persistent collections trade memory for structural sharing
Where LightClone fits: Once you've committed to immutable data structures, LightClone provides compile-time enforcement that cloning is cheap. It catches accidental String or Vec fields that would silently introduce expensive deep clones.
Performance
LightClone has zero runtime overhead—.light_clone() compiles to identical code as .clone().
The real performance benefit comes from using immutable data structures:
| Scenario | Immutable | Standard | Difference |
|---|---|---|---|
| Clone 10KB string | 11 ns | 83 ns | 7x faster |
| Clone struct with 50 levels of nesting | 15 ns | 1.9 µs | 128x faster |
| Clone 10K element vector | 41 ns | 622 ns | 15x faster |
| Clone 10K element hashmap | 15 ns | 2.2 µs | 148x faster |
Mutation has trade-offs—persistent collections are slower for small, mutation-heavy workloads but catch up as data grows. See BENCHMARKS.md for detailed comparisons.
Minimum Supported Rust Version
Rust 1.70.0. The rpds feature requires Rust 1.85+ due to upstream dependencies.
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.