Skip to main content
Version: 0.6.0

Mutability

Variables in noir can be declared mutable via the mut keyword. Mutable variables can be reassigned to via an assignment expression.

let x = 2;
x = 3; // error: x must be mutable to be assigned to

let mut y = 3;
let y = 4; // OK

The mut modifier can also apply to patterns:

let (a, mut b) = (1, 2);
a = 11; // error: a must be mutable to be assigned to
b = 12; // OK

let mut (c, d) = (3, 4);
c = 13; // OK
d = 14; // OK

// etc.
let MyStruct { x: mut y } = MyStruct { x: a }
// y is now in scope

Note that mutability in noir is local and everything is passed by value, so if a called function mutates its parameters then the parent function will keep the old value of the parameters.

fn main() -> Field {
let x = 3;
helper(x);
x // x is still 3
}

fn helper(mut x: i32) {
x = 4;
}

Comptime values

Comptime value are values that are known at compile-time. This is different to a witness which changes per proof. If a comptime value that is being used in your program is changed, then your circuit will also change.

Below we show how to declare a comptime value:

fn main() {
let a: comptime Field = 5;

// `comptime Field` can also be inferred:
let a = 5;
}

Note that variables declared as mutable may not be comptime:

fn main() {
// error: Cannot mark a comptime type as mutable
let mut a: comptime Field = 5;

// a inferred as a private Field here
let mut a = 5;
}

Globals

Noir also supports global variables. However, they must be compile-time variables. If comptime is not explicitly written in the type annotation the compiler will implicitly specify the declaration as compile-time. They can then be used like any other compile-time variable inside functions. The global type can also be inferred by the compiler entirely. Globals can also be used to specify array annotations for function parameters and can be imported from submodules.

global N: Field = 5; // Same as `global N: comptime Field = 5`

fn main(x : Field, y : [Field; N]) {
let res = x * N;

constrain res == y[0];

let res2 = x * mysubmodule::N;
constrain res != res2;
}

mod mysubmodule {
use dep::std;

global N: Field = 10;

fn my_helper() -> comptime Field {
let x = N;
x
}
}

Why only local mutability?

Witnesses in a proving system are immutable in nature. Noir aims to closely mirror this setting without applying additional overhead to the user. Modeling a mutable reference is not as straightforward as on conventional architectures and would incur some possibly unexpected overhead.