Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/noir-lang/noir/llms.txt

Use this file to discover all available pages before exploring further.

Noir’s module and package system follows the same conventions as Rust’s newer module system. Modules help you organize code across multiple files, and packages group crates that are managed together via Nargo.toml.

Modules

Declaring a module

The compiler does not automatically scan files for modules. You must explicitly declare each module with the mod keyword in the file that uses it. src/main.nr
mod foo;

fn main() {
    foo::from_foo();
}
src/foo.nr
fn from_foo() {}
The compiler sees mod foo and looks for either src/foo.nr or src/foo/mod.nr.
It is an error to have both src/foo.nr and src/foo/mod.nr in the same project.

Sub-modules

A module can declare its own sub-modules. The compiler looks for sub-module files relative to the parent module’s directory: src/main.nr
mod foo;

fn main() {
    foo::from_foo();
}
src/foo.nr
mod bar;

fn from_foo() {}
src/foo/bar.nr
fn from_bar() {}
The module hierarchy looks like:
crate
 ├── main
 └── foo
      ├── from_foo
      └── bar
           └── from_bar

Referencing modules

All modules are accessible from the crate:: namespace. Use crate::path to reference any module from anywhere in the project:
// In src/foo/bar.nr, to call something in src/foo.nr:
use crate::foo::from_foo;
Use super:: to refer to the parent module:
// In src/foo/bar.nr
use super::from_foo;

fn from_bar() {
    from_foo();        // calls foo::from_foo
    super::from_foo(); // equivalent
}

Visibility

Modules are private by default. Use pub or pub(crate) to control visibility:
pub mod foo;      // visible to other crates
pub(crate) mod bar; // visible only within this crate

use statements

Import names from other modules with use:
use std::hash::blake3;
use std::scalar_mul::fixed_base_embedded_curve;
Import multiple items at once with curly braces:
use std::hash::{blake2s, blake3};
Import an entire module:
use ecrecover;
use lib_a;

Re-exporting with pub use

use declarations are private to their containing module by default. Mark them pub or pub(crate) to re-export names:
mod some_module {
    pub use foo::{bar, baz};

    mod foo {
        pub fn bar() {}
        pub fn baz() {}
    }
}

fn main() {
    some_module::bar(); // bar re-exported from foo
    some_module::baz();
}

Crates and packages

Crate types

A crate is the smallest unit the Noir compiler processes. There are three kinds:
Binary crates compile to ACIR circuits that you can prove. They must have a main function.The crate root file must be named src/main.nr.
Library crates define reusable functionality without a main function. They do not compile to ACIR directly.The crate root file must be named src/lib.nr.
Contract crates compile to ACIR and are deployed to the Aztec network. They expose a collection of functions rather than a single main.

Packages

A package is a collection of one or more crates, described by a Nargo.toml file. A package must contain either a library crate or a binary crate, but not both. A minimal Nargo.toml for a binary:
[package]
name = "my_project"
type = "bin"
authors = ["Alice"]
compiler_version = ">=0.38.0"
For a library:
[package]
name = "my_library"
type = "lib"
authors = ["Alice"]

Dependencies

Git dependencies

Specify a git dependency with a tag for reproducible builds:
[dependencies]
bignum = { tag = "v0.8.0", git = "https://github.com/noir-lang/noir-bignum" }
If the package is in a subdirectory of the repo, add directory:
[dependencies]
blob = {
    tag = "v1.2.1",
    git = "https://github.com/AztecProtocol/aztec-packages",
    directory = "noir-projects/noir-protocol-circuits/crates/blob"
}
Always specify a tag. Without one, your build will be non-reproducible — the dependency may change on every compile.

Local (path) dependencies

Reference a local library with path:
[dependencies]
lib_a = { path = "../lib_a" }
Example project layout:
├── binary_crate
│   ├── Nargo.toml
│   └── src
│       └── main.nr
└── lib_a
    ├── Nargo.toml
    └── src
        └── lib.nr

Importing dependencies

After declaring a dependency in Nargo.toml, import it in your Noir code:
use lib_a;

// Or import specific items:
use lib_a::some_function;

Transitive dependencies

When you import a dependency, you also get access to all of that package’s dependencies:
use phy_vector;

fn main(x: u32, y: pub u32) {
    let f = phy_vector::fraction::toFraction(true, 2, 1);
}

The standard library

The Noir standard library (std) is always available. Import items from it with use std::...:
use std::hash::{blake2s, blake3};
use std::field::bn254::assert_lt;
Common standard library modules include:
ModuleContents
std::hashHash functions: blake2s, blake3, sha256, Pedersen, Poseidon
std::fieldField utilities: to_le_bits, bn254 curve-specific functions
std::opsOperator traits: Add, Sub, Mul, wrapping variants
std::collectionsData structures: BoundedVec, HashMap, BTreeSet
std::metaCompile-time metaprogramming API
std::memMemory utilities: zeroed
std::optionThe Option<T> type

Workspaces

A workspace manages multiple related Noir packages in a single repository. Create a top-level Nargo.toml with a [workspace] section:
[workspace]
members = ["crates/a", "crates/b"]
default-member = "crates/a"
Example project structure:
├── crates
│   ├── a
│   │   ├── Nargo.toml
│   │   ├── Prover.toml
│   │   └── src
│   │       └── main.nr
│   └── b
│       ├── Nargo.toml
│       ├── Prover.toml
│       └── src
│           └── main.nr
└── Nargo.toml
  • members — which packages are included in the workspace.
  • default-member — the package processed by commands when no --package flag is given.
Within a workspace, reference sibling libraries using path dependencies:
[dependencies]
lib_a = { path = "../lib_a" }
Avoid nesting regular packages inside a workspace — certain nargo commands walk up to find the topmost Nargo.toml and may run on an unintended package.

Build docs developers (and LLMs) love