Question
I want to convert a Vec<T> with length S into a fixed-size array of type [T; S] in Rust.
For example, I am using a function that returns a 128-bit hash as a Vec<u8>. That vector will always have length 16, and I would like to work with it as a [u8; 16] instead.
Is there a built-in way to do this, similar to as_slice(), or should I write my own function that creates a fixed-size array, copies the vector elements into it, and returns the array?
Short Answer
By the end of this page, you will understand how Rust handles Vec<T>, slices, and fixed-size arrays, and how to convert between them safely. You will also learn when conversion can fail, which standard-library methods to use, and which approach is best when you know the length should be exact.
Concept
In Rust, a Vec<T> and an array [T; N] are related but different types.
Vec<T>is a growable, heap-allocated collection.[T; N]is a fixed-size collection where the length is part of the type.
That difference is important:
- A
Vec<u8>can have any length at runtime. - A
[u8; 16]must have exactly 16 elements.
Because array length is part of the type, Rust cannot automatically assume that an arbitrary vector has the correct size. Even if you know the vector always contains 16 bytes, the compiler still needs a checked conversion.
This matters in real programs because fixed-size arrays are often used for:
- hashes like MD5 or UUID-like byte values
- cryptographic keys and nonces
- network protocol fields with known sizes
- binary parsing where exact byte counts matter
Rust's standard library provides built-in conversion support using TryFrom and try_into(). This is the idiomatic way to convert a slice or vector into an array when the length must match exactly.
If the length does not match, the conversion fails instead of silently truncating or padding. That makes the code safer and more explicit.
Mental Model
Think of a Vec<T> as a bag that can hold any number of items, while [T; N] is a tray with exactly N slots.
A vector might currently contain 16 items, but the type itself does not guarantee that. An array does guarantee it.
So converting a Vec<u8> to [u8; 16] is like checking whether your bag has exactly enough items to fill a 16-slot tray:
- If it has exactly 16 items, the transfer works.
- If it has 15 or 17 items, the transfer should fail.
Rust makes you perform that check explicitly.
Syntax and Examples
The most common modern Rust approach is to use try_into().
Converting a Vec<u8> to [u8; 16]
use std::convert::TryInto;
fn main() {
let bytes: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let array: [u8; 16] = bytes.try_into().expect("expected exactly 16 bytes");
println!("{:?}", array);
}
What this does
bytes.try_into()attempts to convert the vector into a fixed-size array.
Step by Step Execution
Consider this example:
use std::convert::TryInto;
fn main() {
let bytes = vec![10, 20, 30, 40];
let array: [u8; 4] = bytes.try_into().expect("expected 4 bytes");
println!("{:?}", array);
}
Here is what happens step by step:
-
let bytes = vec![10, 20, 30, 40];- A
Vec<u8>is created on the heap. - Its length is 4.
- A
-
bytes.try_into()- Rust attempts to convert the vector into an array.
- The target type is known from the left side:
[u8; 4].
-
Length check
- Rust verifies that the vector has exactly 4 elements.
- Since it does, conversion succeeds.
Real World Use Cases
Fixed-size array conversion appears often in Rust code that deals with exact binary layouts.
Hashes and digests
A hashing function may produce 16 bytes for an MD5 digest or another fixed-length binary value.
use std::convert::TryInto;
fn parse_hash(bytes: Vec<u8>) -> Result<[u8; 16], Vec<u8>> {
bytes.try_into()
}
Cryptography
Keys, initialization vectors, and nonces often have exact sizes such as 16, 24, or 32 bytes.
use std::convert::TryInto;
fn load_key(bytes: Vec<u8>) -> Result<[u8; 32], Vec<u8>> {
bytes.try_into()
}
Binary protocols
A file header or packet field may be exactly 8 bytes.
use std::convert::TryInto;
fn read_header(bytes: &[]) <[; ], std::array::TryFromSliceError> {
bytes.()
}
Real Codebase Usage
In real Rust projects, developers usually choose one of these patterns.
1. Convert as early as possible
If a value must be 16 bytes, convert it near the boundary of your program.
use std::convert::TryInto;
fn decode_hash(input: Vec<u8>) -> Result<[u8; 16], Vec<u8>> {
input.try_into()
}
This keeps the rest of the code strongly typed.
2. Use Result for validation
Instead of panicking, return an error when the length is wrong.
use std::convert::TryInto;
fn validate_token(bytes: &[u8]) -> Result<[u8; 16], std::array::TryFromSliceError> {
bytes.try_into()
}
This is common in APIs, parsers, and libraries.
3. Use guard clauses before conversion
Sometimes developers check the length first for clearer errors.
std::convert::TryInto;
(bytes: <>) <[; ], > {
bytes.() != {
((, bytes.()));
}
(bytes.().())
}
Common Mistakes
Here are common beginner mistakes when converting vectors to arrays in Rust.
Mistake 1: Assuming a Vec<T> is the same as [T; N]
Broken idea:
let v = vec![1, 2, 3, 4];
let a: [u8; 4] = v;
Why it fails:
Vec<u8>and[u8; 4]are different types.- Rust does not perform this conversion implicitly.
Fix:
use std::convert::TryInto;
let v = vec![1, 2, 3, 4];
let a: [u8; 4] = v.try_into().expect("expected 4 items");
Mistake 2: Ignoring the possibility of wrong length
Comparisons
Here is how the related Rust collection types compare.
| Type | Size known at compile time? | Growable? | Owns data? | Best use |
|---|---|---|---|---|
[T; N] | Yes | No | Yes | Exact fixed-size values |
Vec<T> | No | Yes | Yes | Dynamic collections |
&[T] | No | No | No | Borrowed view into contiguous data |
Vec<T> vs [T; N]
- Use
Vec<T>when the number of items can vary.
Cheat Sheet
use std::convert::TryInto;
Convert Vec<T> to [T; N]
let v: Vec<u8> = vec![1, 2, 3, 4];
let a: [u8; 4] = v.try_into().expect("expected 4 items");
- Succeeds only if lengths match exactly.
- Consumes the vector.
Convert &[T] to [T; N]
let v: Vec<u8> = vec![1, 2, 3, 4];
let a: [u8; 4] = v.().().();
FAQ
Can I directly cast a Vec<u8> to [u8; 16] in Rust?
No. They are different types, so you need an explicit checked conversion such as try_into().
What is the idiomatic way to convert a Vec<T> to an array in Rust?
Use TryInto:
let arr: [u8; 16] = vec.try_into()?;
This works when the vector has exactly the right length.
What happens if the vector length is wrong?
The conversion returns an error. If you use unwrap() or expect(), the program will panic.
Should I use as_slice() first?
Use as_slice() if you want to keep the original vector. If you do not need the vector anymore, converting the vector directly is often better.
Does converting from a slice copy the data?
Yes. Converting from &[T] to [T; N] creates a new array value.
Mini Project
Description
Build a small Rust utility that accepts a byte vector and validates whether it represents a 16-byte hash. This demonstrates how to convert runtime-sized data into a strongly typed fixed-size array and reject invalid input safely.
Goal
Create a function that turns a Vec<u8> into a [u8; 16] and reports an error if the length is not exactly 16.
Requirements
- Write a function that takes a
Vec<u8>as input. - Return
Result<[u8; 16], String>from the function. - Reject input that is not exactly 16 bytes long.
- Print the valid hash if conversion succeeds.
- Print a helpful error message if conversion fails.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.
Can a Struct Extend Another Struct in Rust? Composition vs Inheritance
Learn how Rust handles struct reuse without inheritance, using composition, traits, and wrapper structs with practical examples.