WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Tuple utilities for variadic programming in rust, fractioned across several crates to improve compile times.

License

Notifications You must be signed in to change notification settings

LucaCappelletti94/tuplities

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tuplities

Documentation CI Security Audit License: MIT Downloads

Tuple utilities in Rust, fractioned across several crates to improve compile times, providing several useful traits for both variadic programming with tuples as well as flat tuple manipulation.

The main crate tuplities re-exports traits from subcrates in its prelude module, so that the subcrates may be compiled in parallel cutting down on overall build times.

This library is #[no_std] compatible, making it suitable for embedded systems and other environments without the standard library.

[dependencies]
tuplities = "0.1.4"

The library provides several traits for working with tuples:

Variadic Programming with Nested Tuples

One of the most powerful features of tuplities is the ability to perform variadic programming on tuples of arbitrary length by converting them to a nested representation (Head, (Tail...)). This allows you to define traits recursively without needing to implement them for every possible tuple size using macros.

It is important to note that "true" variadic generics are not yet available in Rust. As such, there is still a limit on how large the flat tuples can be, determined by which size-XX feature flags are enabled in this crate. However, this nested approach nevertheless allows for an arbitrary level of recursion while maintaining performant compile times, typically in the order of a few seconds even for large tuple sizes.

Here is an example of how to define a trait that works on tuples of any size where all elements implement Display:

use tuplities::prelude::*;
use core::fmt::Display;

// 1. Define flat & nested traits
trait PrintTuple {
    fn print_tuple(&self);
}
trait PrintNested {
    fn print_nested(&self);
}

// 2. Implement for the base cases
impl PrintNested for () {
    fn print_nested(&self) {}
}
impl<Head: Display> PrintNested for (Head,) {
    fn print_nested(&self) {
        println!("{}", self.0);
    }
}

// 3. Implement for the recursive case
impl<Head, Tail> PrintNested for (Head, Tail)
where
    Head: Display, // Current constraint
    Tail: PrintNested, // Recursive constraint
{
    fn print_nested(&self) {
        println!("{}", self.0);
        self.1.print_nested();
    }
}

// 4. Blanket implementation for nestable tuples
//    whose nested form implements PrintNested
impl<T> PrintTuple for T
where
    for<'a> &'a T: NestTuple<Nested: PrintNested>,
{
    fn print_tuple(&self) {
        self.nest().print_nested();
    }
}

// Usage
let tuple = (42, "hello", true);
tuple.print_tuple();
let another_tuple = ();
another_tuple.print_tuple();
let yet_another_tuple = ("only one element",);
yet_another_tuple.print_tuple();

This pattern is extensively used in libraries like diesel-builders to build complex, type-safe abstractions over tuples of varying sizes.

Comparison with Macro-based Approach

Without nested tuples, achieving the same result typically requires defining a recursive macro that implements the trait for every tuple size up to a maximum limit. This approach has several downsides:

  1. Code Bloat: The macro generates a separate implementation for each tuple size, leading to a large amount of generated code.
  2. Compile Times & Quadratic Bounds Growth: The compiler has to process all these implementations, which significantly increases compile times. The number of trait bounds generated increases quadratically with the maximum tuple size N (proportional to N^2), as the macro generates implementations for all sizes from 0 to N, and size k requires k bounds.
  3. Maintenance: Writing and maintaining complex macros can be error-prone and difficult to debug.
  4. Code Coverage: Code coverage tools often struggle to provide meaningful information for code generated via macros, making it harder to ensure your variadic logic is fully tested.

Here is how the same trait would be implemented using macros:

use core::fmt::Display;

trait PrintTuple {
    fn print_tuple(&self);
}

macro_rules! impl_print_tuple {
    // Base case: empty tuple
    () => {
        impl PrintTuple for () {
            fn print_tuple(&self) {}
        }
    };
    // Recursive step: implement for (Head, Tail...) and recurse
    ($Head:ident $(, $Tail:ident)*) => {
        impl<$Head: Display, $($Tail: Display),*> PrintTuple for ($Head, $($Tail,)*) {
            fn print_tuple(&self) {
                #[allow(non_snake_case)]
                let ($Head, $($Tail,)*) = self;
                println!("{}", $Head);
                $(println!("{}", $Tail);)*
            }
        }

        // Recurse for smaller tuple size
        impl_print_tuple!($($Tail),*);
    };
}

// Invoke once for the maximum size you want to support (e.g., 12)
impl_print_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);

Features

The crate provides optional features to enable specific traits. All features are enabled by default for convenience, but can be selectively disabled to reduce compile time and binary size.

Trait Features

The following features enable individual trait crates:

  • clone: Enables TupleClone trait
  • copy: Enables TupleCopy trait
  • debug: Enables TupleDebug trait
  • tuple-default: Enables TupleDefault trait
  • eq: Enables TupleEq trait
  • flatten-nest: Enables FlattenNestedTuple, NestTuple, NestedTupleIndex, NestedTupleIndexMut, NestedTupleTryFrom, NestedTupleTryInto, NestTupleMatrix, FlattenNestedTupleMatrix, NestedTupleOption, IntoNestedTupleOption, and NestedTupleOptionWith traits
  • from: Enables TupleFrom and TupleInto traits
  • hash: Enables TupleHash trait
  • mut: Enables TupleMut and TupleMutMap traits
  • option: Enables TupleOption and IntoTupleOption traits
  • ord: Enables TupleOrd trait
  • partial-eq: Enables TuplePartialEq trait
  • partial-ord: Enables TuplePartialOrd trait
  • remove: Enables TupleRemove trait
  • insert: Enables TupleInsert trait
  • len: Enables TupleLen, UnitTuple, SingletonTuple, and PairTuple traits
  • index: Enables TupleIndex, TupleIndexMut, FirstTupleIndex, and LastTupleIndex traits
  • pop-front: Enables TuplePopFront, TupleRefFront, and TupleMutFront traits
  • pop-back: Enables TuplePopBack, TupleRefBack, and TupleMutBack traits
  • push-front: Enables TuplePushFront trait
  • push-back: Enables TuplePushBack trait
  • ref: Enables TupleRef and TupleRefMap traits
  • replicate: Enables TupleReplicate trait
  • reverse: Enables TupleReverse trait
  • row: Enables TupleRow, TupleRowMut, FirstTupleRow, and LastTupleRow traits
  • split: Enables TupleSplit trait
  • try-from: Enables TupleTryFrom and TupleTryInto traits

Size Features

Additionally, the crate provides features to generate trait implementations for tuples up to different sizes: 8 (default), 16, 32, 48, 64, 96, or 128 elements. Use the size-XX features to enable larger tuple support.

[dependencies]
tuplities = { version = "0.1.4", default-features = false, features = ["clone", "index", "size-32"] }

Performance

Compile times scale with tuple size due to code generation. Below are measured build times for different maximum tuple sizes captured via the project's measure_compile_times.sh script on a typical development machine. Values are approximate and represent wall-clock real times. If you only plan to use variadic nested tuples, you may prefer to use only the tuplities-flatten-nest crate, which has significantly lower compile times.

tuplities (root crate)

Max Tuple Size Compile Time
8 (default) ~4.07s
16 ~2.71s
32 ~3.29s
48 ~4.55s
64 ~6.83s
96 ~15.75s
128 ~32.44s

tuplities-flatten-nest (flatten-nest crate only)

Max Tuple Size Compile Time
8 (default) ~3.18s
16 ~2.45s
32 ~2.60s
48 ~2.86s
64 ~3.40s
96 ~5.46s
128 ~9.52s

Architecture

The project is split into multiple crates for improved compile times:

  • tuplities/: Main crate that re-exports traits from subcrates
  • tuplities-{trait_name}/: Individual crates providing specific traits
  • tuplities-derive/: Procedural macro crate that generates trait implementations

License

This project is licensed under the MIT License. See the LICENSE file for details.

Contribution

Contributions are welcome! Please open issues or pull requests on the GitHub repository.

About

Tuple utilities for variadic programming in rust, fractioned across several crates to improve compile times.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published