Subject
arrayvec provides two fixed-capacity, inline-allocated containers:
ArrayVec<T, CAP> (a vector backed by an array of MaybeUninit<T>) and
ArrayString<CAP> (a UTF-8 string with the same backing strategy). The
capacity is a const generic, range-limited to u32::MAX, and the
structures store their length inline as a u32. The crate is
no_std-compatible (default feature std only enables std::io::Write
and std::error::Error impls). Optional features add serde, borsh,
and zeroize trait impls.
Methodology
The published crate contents were compared against the upstream Git
repository at the commit recorded in .cargo_vcs_info.json (tag 0.7.6)
using diff. All seven files under src/ (~1,800 lines, mostly
arrayvec.rs and array_string.rs) were read in full, with explicit
focus on every unsafe block, raw-pointer arithmetic site, MaybeUninit
use, and Drop impl. The tests/ directory (~1,600 lines of unit and
integration tests including the serde/borsh feature tests) and the
.github/workflows/ci.yml configuration were surveyed.
For the suspected ZST drop-count bug found in extend_from_iter, a
reproducer (tmp/zst-repro) was written and executed against this exact
crate version, instrumenting a Drop-implementing ZST with AtomicUsize
counters and comparing extend against push baseline.
Tools used: openvet 0.6.0 for workspace and audit data management;
diff (GNU diffutils) for byte-level comparison between contents/ and
vcs/; git (2.51) for the upstream checkout; grep/ripgrep for
capability surveys; cargo / rustc for compiling and running the ZST
reproducer.
Results
The comparison between the published crate contents and the upstream
repository shows that all source, test, and license files match
byte-for-byte; the only diff is the standard cargo Cargo.toml
normalisation and the cargo-emitted .cargo_vcs_info.json /
Cargo.toml.orig pair.
The crate ships no binary artefacts (justifying has-binaries) and the
manifest sets build = false. There is no build.rs, no
[lib] proc-macro = true, and no procedural-macro attributes; cargo runs
no compile- or install-time hooks (justifying has-build-exec and
has-install-exec).
The source contains no network code (no std::net, reqwest, ureq),
no process invocation (no std::process), no environment-variable access
(no std::env), no filesystem I/O (the only reference to
std::path::Path is an AsRef<Path> impl on ArrayString that performs
no I/O), and no cryptographic operations or implementations. The
zeroize feature delegates entirely to the zeroize crate's traits. No
threads are spawned, no async runtime is used, no concurrency primitives
are implemented; the only concurrency-relevant code is the
unsafe impl Send/Sync for Drain which mirrors std::vec::Drain. These
observations justify uses-network, uses-filesystem, uses-environment,
uses-exec, uses-jit, uses-interpreter, uses-crypto,
uses-concurrency, impl-crypto, impl-parser, impl-interpreter,
impl-jit, impl-protocol, impl-algorithm, and impl-concurrency.
ArrayVec and ArrayString are concrete data-structure implementations
(justifying impl-datastructure). All operations are documented O(1) or
O(n) and degrade only with input size, not adversarial ordering
(justifying datastructure-impl-bounds). The crate makes pervasive use of
unsafe for raw-pointer manipulation, MaybeUninit initialisation
tracking, and manual Send/Sync impls (justifying uses-unsafe).
Memory safety of the unsafe code was reviewed: the MaybeUninit-backed
buffer, length-tracked initialisation prefix, ptr::write/ptr::read
element movement, panic-safe Drop (using set_len(0) before
drop_in_place in IntoIter, and ScopeExitGuard in
extend_from_iter), and the BackshiftOnDrop pattern in retain all
match the corresponding std::vec::Vec idioms (justifying unsafe-safe
and datastructure-impl-safe). Unsafe is constrained to operations that
genuinely require it (justifying unsafe-minimal). The crate ships unit
tests in src/ modules and a tests/ directory with integration tests
covering serde/borsh feature paths, justifying has-unit-tests and
has-integration-tests. There is no fuzz/ harness and no
proptest/quickcheck use, justifying has-fuzz-tests and
has-property-tests. CI runs the full test suite under Miri with all
features (justifying unsafe-tested and datastructure-impl-tested).
Two low-severity findings were recorded. Finding FINDING-1 (correctness)
documents a ZST drop-count bug in extend_from_iter: the conditional
skip of ptr::write(elt) for zero-sized types causes the local elt to
be dropped at scope exit while guard.data is still incremented, leading
to a second Drop call on each "slot" when the ArrayVec itself is
later dropped. The bug was confirmed with a runnable reproducer against
this crate version (3 constructions, 6 drops via extend; 3
constructions, 3 drops via push). The impact is limited to non-Copy
ZSTs with side-effecting Drop — an uncommon pattern — and is not
memory-unsafe, but it violates the Vec-like contract that each element
is dropped exactly once. This justifies datastructure-impl-correct =
false. Finding FINDING-2 (quality) notes that most unsafe blocks lack
inline safety comments justifying their invariants, justifying
unsafe-documented = false.
No malicious or deliberately harmful behaviour was found in the source,
tests, or build configuration (justifying is-benign).
Conclusion
arrayvec is a well-scoped, narrow-purpose crate that closely mirrors
std::vec::Vec semantics for fixed-capacity, stack-storable containers.
The unsafe code is necessary, scoped tightly, and exercised under Miri in
CI. The only correctness defect identified is a niche ZST double-drop in
extend; consumers who do not use non-Copy ZSTs with side-effecting
Drop are unaffected. The lack of inline safety comments is the main
quality concern.