Crates.io | stackvec |
lib.rs | stackvec |
version | 0.2.1 |
source | src |
created_at | 2018-10-28 23:53:34.436173 |
updated_at | 2018-11-11 20:39:08.841124 |
description | A crate to use stack-allocated Vectors (performance and/or no-std) |
homepage | https://github.com/danielhenrymantilla/stackvec-rs |
repository | https://github.com/danielhenrymantilla/stackvec-rs |
max_upload_size | |
id | 93226 |
size | 10,552,863 |
[stack;vec]
A rust crate to use stack-allocated vectors (to improve performance and/or when there is no std)
Rust stack/inline arrays don't implement 2 very useful iterator-related interfaces:
IntoIterator<Item = T> for [T; n]
.into_iter()
instead of .iter().cloned()
(which, by the way, can only be used when T: Clone
, and requires cloning, which may be expensive)extern crate stackvec; use ::stackvec::prelude::*;
fn main ()
{
// An array of vectors (potentially expensive to clone)
let vecs_array = [
vec![1, 2, 3, 4],
vec![],
vec![5, 6],
];
// Collect / chain all the vectors together
let flattened: Vec<u8> = vecs_array
.into_iter() // Needs stackvec (line 1)
.flatten()
.collect()
;
assert_eq!(flattened, vec![1, 2, 3, 4, 5, 6]);
}
FromIterator for [T; n]
collect
ing into an array
TryFromIterator
trait (providing try_collect
).
extern crate stackvec; use ::stackvec::prelude::*;
fn main () { let array: [_; 3] = [1, 2, 3];
let doubled: [_; 3] = array
.iter()
.map(|&x| 2 * x)
.try_collect() // Needs stackvec (line 1)
.expect("Missing elements to collect")
;
assert_eq!(doubled, [2, 4, 6]);
}
The reason for that is that both interfaces need a structure being able to hold
the partially iterated state: i.e., incomplete arrays. Those have (statically-allocated) memory that might not be initialized: so they are, in a way, like Vec
tors (except for the fact that their (initial) capacity is fixed and cannot be changed)
That's why having those nice iterator interfaces requires writing down a slot-accurate memory ownership management logic very similar to Vec
's : hence the StackVec
.
By exposing the underlying StackVec
needed by the aformentioned interfaces, we get full access to a stack-allocated Vec
, which can also be useful on its own, since it avoids heap allocation:
the heap is a mutable global state and in multi-threaded environments locks are involved,
it may require (slow) system allocation
The performance gain (from using StackVec
instead of Vec
) is not always guaranteed, since:
Vec
is the cornerstone of Rust's std library collection and has extremely efficient code written so that LLVM can easily optimize its usage
Rust's allocator is also incredibly well optimised so the performance penalties from bins management and system allocations (and the locks in a multi-threaded environment) are quite well amortized on average.
Vec
vs StackVec
basic benchmark$ cargo +nightly bench --features nightly
test vec_extend ... bench: 64,129 ns/iter (+/- 3,069)
test vec_from_iter ... bench: 65,569 ns/iter (+/- 3,761)
test array_from_iter ... bench: 358,993 ns/iter (+/- 6,916)
test stackvec_extend ... bench: 360,105 ns/iter (+/- 17,489)
test stackvec_from_iter ... bench: 369,585 ns/iter (+/- 40,894)
test stackvec_extend_by_ref ... bench: 374,226 ns/iter (+/- 11,686)
test vec_extend_by_ref ... bench: 863,362 ns/iter (+/- 32,483)
Add this line to your Cargo.toml
(under [dependencies]
):
stackvec = "0.2.1"
stackvec
improves all the arrays with less than 1000 elements. This leads to longer compilation times. If this is an issue, and you don't really plan on using arbitrary-length arrays but at fixed multiples of 100 or powers of 2, you can depend on a "lighter" stackvec
using the following line instead:
stackvec = { version = "0.2.1", default-features = false }
Add this to your .rs
code:
extern crate stackvec;
use ::stackvec::prelude::*;
See the source files for the examples
You can run each example (example_name.rs
) with:
$ cargo run --example example_name