# 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. 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.

## Primitive Types

A primitive type represents a single value. They can be private or public.

### The Field Type

The field type corresponds to the native field type of the proving backend.

The size of a Noir field depends on the elliptic curve's finite field for the proving backend adopted. For example, a field would be a 254-bit integer when paired with the default TurboPlonk backend that spans the Grumpkin curve.

Fields support integer arithmetic and are often used as the default numeric type in Noir:

fn main(x : Field, y : Field)  {
let z = x + y;
}


x, y and z are all private fields in this example. Using the let keyword we defined a new private value z constrained to be equal to x + y.

If proving efficiency is of priority, fields should be used as a default for solving problems. Smaller integer types (e.g. u64) incur extra range constraints.

### Integer Types

An integer type is a range constrained field type. The Noir frontend currently supports unsigned, arbitrary-sized integer types.

An integer type is specified first with the letter u, indicating its unsigned nature, followed by its length in bits (e.g. 32). For example, a u32 variable can store a value in the range of $$[0,2^{32}-1]$$:

fn main(x : Field, y : u32) {
let z = x as u32 + y;
}


x, y and z are all private values in this example. However, x is a field while y and z are unsigned 32-bit integers. If y or z exceeds the range $$[0,2^{32}-1]$$, proofs created will be rejected by the verifier.

Note: The default TurboPlonk backend supports both even (e.g. u16, u48) and odd (e.g. u5, u3) sized integer types.

### The Boolean Type

The bool type in Noir has two possible values: true and false:

fn main() {
let t = true;
let f: bool = false;
}


Note: When returning a boolean value, it will show up as a value of 1 for true and 0 for false in Verifier.toml.

The boolean type is most commonly used in conditionals like if expressions and constrain statements. More about conditionals is covered in the Control Flow and Constrain Statement sections.

## The String Type

Strings in Noir are fairly basic with their main use being for debugging via std::println.. Since circuit inputs need to be known at compile time, the string length for an input must be hardcoded into the circuit, like so:

// **input**
// field = "hello"

fn main(string: str<5>) {
let hello = "hello";
constrain string == hello;
}



String manipulation isn't available at this time, but as long as you make the variable mut, you can replace it.

## Compound Types

A compound type groups together multiple values into one type. Elements within a compound type can be private or public.

### The Array Type

An array is one way of grouping together values into one compound type. Array types can be inferred or explicitly specified via the syntax [<Type>; <Size>]:

fn main(x : Field, y : Field) {
let my_arr = [x, y];
let your_arr: [Field; 2] = [x, y];
}


Here, both my_arr and your_arr are instantiated as an array containing two Field elements.

Array elements can be accessed using indexing:

fn main() {
let a = [1, 2, 3, 4, 5];

let first = a[0];
let second = a[1];
}


All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group a Field value and a u8 value together for example.

### The Tuple Type

A tuple collects multiple values like an array, but with the added ability to collect values of different types:

fn main() {
let tup: (u8, u64, Field) = (255, 500, 1000);
}


One way to access tuple elements is via destructuring using pattern matching:

fn main() {
let tup = (1, 2);

let (one, two) = tup;

let three = one + two;
}


Another way to access tuple elements is via direct member access, using a period (.) followed by the index of the element we want to access. Index 0 corresponds to the first tuple element, 1 to the second and so on:

fn main() {
let tup = (5, 6, 7, 8);

let five = tup.0;
let eight = tup.3;
}


### Structs

A struct also allows for grouping multiple values of different types. Unlike tuples, we can also name each field.

Note: The usage of field here refers to each element of the struct and is unrelated to the field type of Noir.

Defining a struct requires giving it a name and listing each field within as <Key>: <Type> pairs:

struct Animal {
hands: Field,
legs: Field,
eyes: u8,
}


An instance of a struct can then be created with actual values in <Key>: <Value> pairs in any order. Struct fields are accessible using their given names:

fn main() {
let legs = 4;

let dog = Animal {
eyes: 2,
hands: 0,
legs,
};

let zero = dog.hands;
}


Structs can also be destructured in a pattern, binding each field to a new variable:

fn main() {
let Animal { hands, legs: feet, eyes } = get_octopus();

let ten = hands + feet + eyes as u8;
}

fn get_octopus() -> Animal {
let octopus = Animal {
hands: 0,
legs: 8,
eyes: 2,
};

octopus
}


The new variables can be bound with names different from the original struct field names, as showcased in the legs --> feet binding in the example above.