Skip to main content
Functions in Noir follow Rust semantics. Unlike Rust, Noir does not support early return statements — the last expression in a function body is its return value.

Basic syntax

Declare a function with the fn keyword:
Parameters must include explicit types, separated by commas. The return type follows the -> arrow:
Use _ or a _-prefixed name for unused parameters:

Visibility

By default, functions are visible only within their package. Use pub to expose them to other packages, or pub(crate) to expose them within the same crate only:

The main function

In binary crates, main is the entry point. All its parameters must have a size known at compile time:
Tests don’t differentiate between main and other functions — a test can pass even when the program itself would fail to compile or prove. Always run nargo check to validate your main signature.

Call expressions

Call a function by name, passing arguments:

Methods on structs

Define methods on any struct using impl blocks. The first parameter self refers to the receiver:

Specialization on generics

Implementations can be specialized for specific generic type arguments:
Overlapping impl blocks — for example, one for Foo<u32> and a blanket impl<T> Foo<T> — are not allowed. Noir must be able to unambiguously select which implementation to use.

Lambdas

Lambdas are anonymous functions using the |args| expr syntax:
Lambdas can have a block body:

Closures

A lambda that captures variables from its enclosing scope is called a closure:
The closure’s type encodes its capture environment as fn[Env](args) -> ret. To accept both regular functions and closures, make the environment type generic:

Mutability

Variables are immutable by default. Declare mutable variables with mut:
Mutability in Noir is local — mutating a parameter inside a function does not affect the caller:
Use mutable references (&mut T) for non-local mutation:

Global variables

Globals are module-level constants with explicit types:
Globals initialized to integer literals can be used to specify array lengths:
Use pub or pub(crate) to control global visibility:

Attributes

Attributes apply metadata to functions using #[attribute] syntax. Common built-in attributes include:
Marks a function as a test case, runnable with nargo test:
Declares a function whose body is provided by the backend (a black-box function):
Declares a function that is built into the compiler:
Any comptime function can be used as an attribute. The function receives the annotated item and can transform it:

Unconstrained functions

Unconstrained functions execute outside the constraint system. They are useful for computation that is too expensive to prove directly — for example, performing division by using a hint that is later verified:
Unconstrained code is not proven. It runs as a hint to generate witnesses, but the result must be explicitly verified with assertions in constrained code. Use unsafe { } blocks to call unconstrained functions from constrained contexts.
Unconstrained functions support additional control flow not available in constrained code:
  • loop / while loops
  • break and continue in loops
  • Dynamic-length operations