Library / SDK
rust-fuzz/arbitrary avatar
rust-fuzz/arbitrary

arbitrary: turning fuzzer byte buffers into typed Rust values

Generating structured data from arbitrary, unstructured input.

887 stars95 forksRustApache-2.0

At a glance

What is it?
The arbitrary crate is a trait for generating structured data from unstructured input, built so a coverage guided fuzzer like libFuzzer or AFL can hand it raw bytes and get back well typed, valid values. The derive macro is behind a cargo feature, field level control is done through attributes, and the crate guarantees to compile on stable Rust 1.71.0 or newer.
Who is it for?
arbitrary suits a Rust project doing coverage guided fuzzing where the interesting part of the input is a structured value rather than an opaque buffer, since it lets you write property assertions about types instead of about byte layouts. It does not suit a project that only needs random data in tests, where a random value generator is simpler, and it does not replace the fuzzer itself.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 16 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The trait that turns bytes into a value of your type

Arbitrary is a Rust crate that lets you construct arbitrary instances of a type. The stated purpose is to be combined with a fuzzer such as libFuzzer and cargo-fuzz or AFL, and to help you turn the raw, untyped byte buffers those fuzzers produce into well typed, valid, structured values. That combination is the point: structure aware test case generation on top of a coverage guided, mutation based fuzzer. The example the README gives is a colour conversion library with an Rgb struct, and the suggestion is to implement Arbitrary for Rgb so a test function can take arbitrary Rgb instances and assert a property, for example that RGB converted to HSL and converted back to RGB always ends up exactly where it started. The crate is a single trait, the API documentation lives on docs.rs, and the primary language of the repository is Rust.

Deriving the trait needs a cargo feature you have to ask for

Automatically deriving Arbitrary is the recommended way to implement it for your types, and it is not on by default. The derive lives in a separate crate, derive_arbitrary, and the feature flag has to be enabled explicitly in the dependency declaration:

toml
# Cargo.toml

[dependencies]
arbitrary = { version = "1", features = ["derive"] }

With that in place, the type gets an attribute and nothing else changes:

rust
// rgb.rs

use arbitrary::Arbitrary;

#[derive(Arbitrary)]
pub struct Rgb {
    pub r: u8,
    pub g: u8,
    pub b: u8,
}

The manifest makes the split explicit, with the derive feature listed as turning on support for the derive annotation and pulling in the optional derive_arbitrary dependency at roughly the same version as the crate itself, with a comment in the manifest reminding you to keep the two versions in step. The crate version is 1.5.0, the edition is 2021, and the crate is categorised as a development tool for testing.

Field attributes control how a value is generated

The derive is useful but not sufficient on its own, and the field level attributes are the part that decides whether your generated values are interesting. The documentation gives four patterns. The default attribute sets the field to its Default value, which is the right choice for a type that does not implement Arbitrary or a field you do not care about. The value attribute sets a constant, for example 255 for a channel that must be full. The with attribute takes a function of type fn(&mut Unstructured) -> arbitrary::Result<T>, where T is the field type, and there is a closure form as a shortcut to avoid writing that function out:

rust
#[derive(Arbitrary)]
pub struct Rgba {
    // set `r` to Default::default()
    #[arbitrary(default)]
    pub r: u8,

    // set `g` to 255
    #[arbitrary(value = 255)]
    pub g: u8,

    // Generate `b` with a custom function of type
    //
    //    fn(&mut Unstructured) -> arbitrary::Result<T>
    //
    // where `T` is the field's type.
    #[arbitrary(with = arbitrary_b)]
    pub b: u8,

    // Generate `a` with a custom closure (shortcut to avoid a custom function)
    #[arbitrary(with = |u: &mut Unstructured| u.int_in_range(0..=64))]
    pub a: u8,
}

fn arbitrary_b(u: &mut Unstructured) -> arbitrary::Result<u8> {
    u.int_in_range(64..=128)
}

The int_in_range call is the interesting one, because it is how you bias generation towards the interesting part of a range rather than the full width of the type. A fuzzer spends its budget on values that reach new code, and a field constrained to 64 to 128 instead of the whole range of a byte is a field that produces more distinct states per byte consumed.

Writing the implementation by hand when the derive does not fit

The alternative is to implement the trait yourself, and the shape of the implementation tells you what the trait actually demands of your type. The lifetime parameter on the trait means the implementation is tied to the lifetime of the Unstructured value it reads from:

rust
// rgb.rs

use arbitrary::{Arbitrary, Result, Unstructured};

#[derive(Copy, Clone, Debug)]
pub struct Rgb {
    pub r: u8,
    pub g: u8,
    pub b: u8,
}

impl<'a> Arbitrary<'a> for Rgb {
    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
        let r = u8::arbitrary(u)?;
        let g = u8::arbitrary(u)?;
        let b = u8::arbitrary(u)?;
        Ok(Rgb { r, g, b })
    }
}

Each field is drawn in turn from the same Unstructured value, and the question mark propagates a failure, so an implementation that runs out of input returns an error rather than a partial value. The Result type is imported from the crate rather than from the standard library, and the Unstructured type is the handle to the untyped input. Writing it by hand is what you do when the derive cannot express the constraint you need, for example when two fields have to be consistent with each other rather than independently generated.

What is in the repository, and what the fuzz directory is for

The tree is small and tells you how the project is organised. There is a src directory for the trait itself, a derive directory holding the derive_arbitrary crate, a fuzz directory, an examples directory, a tests directory, a CHANGELOG, a publish.sh script and the two licence files. The manifest declares a workspace with the fuzz directory as a member, which is the standard arrangement for a crate that keeps its own fuzz targets in the same repository, and the fuzz targets are what the trait exists to feed. The examples directory contains a single file, derive_enum.rs, and the manifest registers it as an example that requires the derive feature, so building it without that feature is refused rather than failing confusingly. There is also a test target for the derive, at tests/derive.rs, registered the same way, and a dev dependency on exhaustigen, a generator that produces exhaustively many values for a type, which is how the test suite can check that a derived implementation covers its value space.

MSRV 1.71.0, and a policy on raising it

The minimum supported Rust version is stated precisely, and the policy around it is spelled out. The crate is guaranteed to compile on stable Rust 1.71.0 and up. It might compile with older versions, but that may change in any new patch release, which is a careful way of saying that older versions are not a supported configuration. The maintainers reserve the right to increment the MSRV on minor releases, and say they will strive to only do it deliberately and for good reasons. The manifest carries the same number as a rust-version field, with a comment in both places reminding you to keep the two in step, which is a detail that matters for a crate with this many downstream users. The licence is dual MIT or Apache-2.0 at your choice, with a LICENSE-MIT and a LICENSE-APACHE file at the root, and contributions are dual licensed the same way unless you state otherwise. The last push was on 2026-10-06.

Where this differs from just using random numbers in tests

The obvious alternative is to fill your test inputs with random values from a random number generator, and for many codebases that is enough. The difference appears when the input has structure that the code under test depends on. A fuzzer hands you a byte buffer whose contents it chose because they reached interesting code, and arbitrary is the layer that decides how those bytes map onto your type. Generating a random Rgb with three independent random bytes gives you values spread evenly across a colour cube, most of which are unremarkable. Constraining the alpha channel to 0 to 64 and the blue channel to 64 to 128, as the attribute examples do, biases generation towards the region of the space where the interesting cases live, and the fuzzer then keeps the bytes that reached new code. The two approaches also differ in what they can express. A random generator cannot produce a value that is valid only if a constraint holds across two fields, because it has no notion of the constraint. An Arbitrary implementation can, because you are writing the code that draws the fields.

Editorial conclusion

arbitrary suits a Rust project doing coverage guided fuzzing where the interesting part of the input is a structured value rather than an opaque buffer, since it lets you write property assertions about types instead of about byte layouts. It does not suit a project that only needs random data in tests, where a random value generator is simpler, and it does not replace the fuzzer itself. Before adopting it, check that the types you want to generate implement the trait or can derive it, and note that the derive lives in a separate crate behind a feature, so the dependency line has to ask for it explicitly.

Frequently asked questions

What is the arbitrary crate used for?

It lets you construct arbitrary instances of a type, and it is primarily intended to be combined with a fuzzer like libFuzzer and cargo-fuzz or AFL, turning the raw untyped byte buffers they produce into well typed, valid, structured values so you can combine structure aware test case generation with coverage guided fuzzing.

How do I enable the derive macro for arbitrary?

Add the derive feature to the dependency, with arbitrary = { version = "1", features = ["derive"] }, then put #[derive(Arbitrary)] on your types. The derive lives in a separate optional crate, derive_arbitrary, and the feature flag turns it on.

How do I control how a single field is generated in arbitrary?

With field attributes: #[arbitrary(default)] uses the field's Default value, #[arbitrary(value = 255)] sets a constant, and #[arbitrary(with = ...)] takes either a function of type fn(&mut Unstructured) -> arbitrary::Result<T> or a closure, which is how you restrict a field to a range with a call to int_in_range.

Which Rust version does the arbitrary crate support?

It is guaranteed to compile on stable Rust 1.71.0 and up, and the manifest carries the same rust-version value. The maintainers reserve the right to raise the minimum on minor releases but say they will do it deliberately, and older versions may stop compiling in any patch release.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. Project website
  4. README
  5. rust-fuzz/arbitrary on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rust-fuzz-arbitrary.svg)](https://hysenlabs.com/projects/rust-fuzz-arbitrary)

Was this useful?