Data Types
Every value in Noir has a type, which determines which operations are valid for it.
All values in Noir are fundamentally composed of Field
elements. For a more approachable
developing experience, abstractions are added on top to introduce different data types in Noir.
Noir has two category of data types: primitive types (e.g. Field
, integers, bool
) and compound
types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or
public.
Private & Public Types
A private value is known only to the Prover, while a public value is known by both the
Prover and Verifier. Mark values as private
when the value should only be known to the prover. All
primitive types (including individual fields of compound types) in Noir are private by default, and
can be marked public when certain values are intended to be revealed to the Verifier.
Note: For public values defined in Noir programs paired with smart contract verifiers, once the proofs are verified on-chain the values can be considered known to everyone that has access to that blockchain.
Public data types are treated no differently to private types apart from the fact that their values will be revealed in proofs generated. Simply changing the value of a public type will not change the circuit (where the same goes for changing values of private types as well).
Private values are also referred to as witnesses sometimes.
Note: The terms private and public when applied to a type (e.g.
pub Field
) have a different meaning than when applied to a function (e.g.pub fn foo() {}
).The former is a visibility modifier for the Prover to interpret if a value should be made known to the Verifier, while the latter is a visibility modifier for the compiler to interpret if a function should be made accessible to external Noir programs like in other languages.
pub Modifier
All data types in Noir are private by default. Types are explicitly declared as public using the
pub
modifier:
fn main(x : Field, y : pub Field) -> pub Field {
x + y
}
In this example, x
is private while y
and x + y
(the return value) are public. Note
that visibility is handled per variable, so it is perfectly valid to have one input that is
private and another that is public.
Note: Public types can only be declared through parameters on
main
.
Type Aliases
A type alias is a new name for an existing type. Type aliases are declared with the keyword type
:
type Id = u8;
fn main() {
let id: Id = 1;
let zero: u8 = 0;
assert(zero + 1 == id);
}
Type aliases can also be used with generics:
type Id<Size> = Size;
fn main() {
let id: Id<u32> = 1;
let zero: u32 = 0;
assert(zero + 1 == id);
}
Type aliases can even refer to other aliases. An error will be issued if they form a cycle:
// Ok!
type A = B;
type B = Field;
type Bad1 = Bad2;
// error: Dependency cycle found
type Bad2 = Bad1;
// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2
By default, like functions, type aliases are private to the module the exist in. You can use pub
to make the type alias public or pub(crate)
to make it public to just its crate:
// This type alias is now public
pub type Id = u8;
Wildcard Type
Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using _
as a marker, indicating where we still want the compiler to infer the type.
let a: [_; 4] = foo(b);
BigInt
You can achieve BigInt functionality using the Noir BigInt library.