Learn how to declare functions in Noir — including parameters, return types, visibility, methods on structs, lambdas, attributes, and unconstrained functions.
Use this file to discover all available pages before exploring further.
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.
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:
pub fn foo() {} // visible to all packagespub(crate) fn bar() {} // visible only within this crate
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.
Implementations can be specialized for specific generic type arguments:
struct Foo<T> {}impl Foo<u32> { fn foo(self) -> Field { 1 }}impl Foo<u64> { fn foo(self) -> Field { 2 }}fn main() { let f1: Foo<u32> = Foo{}; let f2: Foo<u64> = Foo{}; assert(f1.foo() + f2.foo() == 3);}
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.
A lambda that captures variables from its enclosing scope is called a closure:
fn main() { let x = 100; let closure = || x + 150; assert(closure() == 250);}
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:
fn foo<Env>(f: fn[Env]() -> Field) -> Field { f()}fn main() { let (x, y) = (50, 50); assert(foo(|| x + y) == 100); assert(foo(|| 60) == 60);}
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:
fn main(x: u64, y: u64) { let div = unsafe { my_div(x, y) }; // Verify the hint assert(div * y == x);}unconstrained fn my_div(x: u64, y: u64) -> u64 { x / y}
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
unconstrained fn count_down(start: u32) -> u32 { let mut i = start; while i > 0 { i -= 1; } i}