#vec #container #type-erasure #any #no-alloc #no-std #send-sync

no-std any_vec

Type erased vector. Most operations can be done without type knowledge. Mostly zero overhead.

17 releases (breaking)

0.14.0 May 10, 2024
0.13.0 Aug 25, 2023
0.12.0 Jun 10, 2023
0.11.0 Aug 26, 2022
0.10.0 Jul 10, 2022

#108 in Data structures

Download history 3155/week @ 2024-07-29 2366/week @ 2024-08-05 1826/week @ 2024-08-12 2303/week @ 2024-08-19 2433/week @ 2024-08-26 1935/week @ 2024-09-02 1731/week @ 2024-09-09 1871/week @ 2024-09-16 1718/week @ 2024-09-23 1746/week @ 2024-09-30 1582/week @ 2024-10-07 1694/week @ 2024-10-14 1518/week @ 2024-10-21 1644/week @ 2024-10-28 1521/week @ 2024-11-04 1478/week @ 2024-11-11

6,359 downloads per month
Used in 3 crates (2 directly)

MIT/Apache

130KB
2.5K SLoC

crates.io license Docs CI

Type erased vector. All elements have the same type.

Designed to be type-erased as far as possible - most operations do not know a concrete type. For example, you can move or copy/clone items from one type-erased vector to another, without ever knowing their type. Or you can erase, swap, move, copy elements inside type-erased vector, etc...

Only type-erased destruct and clone operations have additional overhead of indirect call.

Usage

    let mut vec: AnyVec = AnyVec::new::<String>();
    {
        // Typed operations.
        let mut vec = vec.downcast_mut::<String>().unwrap();
        vec.push(String::from("0"));
        vec.push(String::from("1"));
        vec.push(String::from("2"));
    }
 
    let mut other_vec: AnyVec = AnyVec::new::<String>();
    // Fully type erased element move from one vec to another
    // without intermediate mem-copies.
    let element = vec.swap_remove(0);
    other_vec.push(element);

    // Output 2 1
    for s in vec.downcast_ref::<String>().unwrap(){
        println!("{}", s);
    } 

See documentation for more.

Send, Sync, Clone

You can make AnyVec Sendable, Syncable, Cloneable:

use any_vec::AnyVec;
use any_vec::traits::*;
let v1: AnyVec<dyn Cloneable + Sync + Send> = AnyVec::new::<String>();
let v2 = v1.clone();

This constraints will be applied compiletime to element type:

// This will fail to compile. 
let v1: AnyVec<dyn Sync + Send> = AnyVec::new::<Rc<usize>>();

Non-Clonable AnyVec has a size 1 pointer smaller.

LazyClone

Whenever possible, any_vec type erased elements can be lazily cloned:

 let mut v1: AnyVec<dyn Cloneable> = AnyVec::new::<String>();
 v1.push(AnyValueWrapper::new(String::from("0")));

 let mut v2: AnyVec<dyn Cloneable> = AnyVec::new::<String>();
 let e = v1.swap_remove(0);
 v2.push(e.lazy_clone());
 v2.push(e.lazy_clone());

MemBuilder

MemBuilder + Mem works like Allocator for AnyVec. But unlike allocator, Mem container-specialized design allows to perform more optimizations. For example, it is possible to make stack-allocated FixedAnyVec and small-buffer-optimized(SBO) SmallAnyVec from AnyVec by just changing MemBuilder:

type FixedAnyVec<Traits = dyn None> = AnyVec<Traits, Stack<512>>;
let mut any_vec: FixedAnyVec = AnyVec::new::<String>();

// This will be on stack, without any allocations.
any_vec.push(AnyValueWrapper::new(String::from("0")))

With help of clone_empty_in you can use stack allocated, or SBO AnyVec as fast intermediate storage for values of unknown type:

fn self_push_first_element<T: Trait + Cloneable>(any_vec: &mut AnyVec<T>){
    let mut tmp = any_vec.clone_empty_in(StackN::<1, 256>);
    tmp.push(any_vec.at(0).lazy_clone());
    any_vec.push(tmp.pop().unwrap());
}

MemBuilder interface, being stateful, allow to make Mem, which can work with complex custom allocators.

no_std + no_alloc

This is no_std library, which can work without alloc too.

Changelog

See CHANGELOG.md for version differences.

Known alternatives

  • type_erased_vec. Allow to store Vec<T> in type erased way, but you need to perform operations, you need to "cast" to concrete type first.
  • untyped_vec. Some operations like len, capacity performed without type knowledge; but the rest - require concrete type.

No runtime deps