> Aztec.nr API reference, v6.0.0 (mainnet and testnet). Index: https://docs.aztec.network/aztec-nr-api/mainnet/llms.txt

# Struct PublicImmutable

Module: `noir_aztec::state_vars`

```rust
pub struct PublicImmutable<T, Context>
{ /* private fields */ }
```

Immutable public values.

This is one of the most basic public state variables. It is similar to an `immutable` or `constant` Solidity state variable.

It represents a public value of type `T` that can be initialized **just once** during the lifetime of the contract, allowing this single value to be read.

Unlike Solidity's `immutable` or `constant`, a `PublicImmutable`'s initialization does not need to occur during contract initialization - it can happen at any point in time (but only once). This also makes it possible to have a [`Map`](/aztec-nr-api/mainnet/noir_aztec/state_vars/struct.Map.md) of `PublicImmutable`s.

## Access Patterns

A value stored in a `PublicImmutable` can be read and initialized from public contract functions.

Unlike [`PublicMutable`](/aztec-nr-api/mainnet/noir_aztec/state_vars/struct.PublicMutable.md) it is **also** possible to read a `PublicImmutable` from a private contract function, though it is not possible to initialize one. A common pattern is to have these functions [enqueue a public self calls](/aztec-nr-api/mainnet/noir_aztec/contract_self/contract_self_private/struct.ContractSelfPrivate.md#enqueue) in which the initialization operation is performed.

For a mutable (with restrictions) variant which also can be read from private functions see [`DelayedPublicMutable`](/aztec-nr-api/mainnet/noir_aztec/state_vars/struct.DelayedPublicMutable.md).

## Privacy

`PublicImmutable` provides zero privacy in terms of the value stored and any public accesses: the entire network can see these and the data involved.

Reading a `PublicImmutable` from a private contract function however is completely private, and leaks zero information about the fact that the value was read.

## Use Cases

This is suitable for any kind of fixed global configuration that needs to be accessible by private contract functions, such as token decimals, related contracts in a multi-contract configuration, etc.

It is also useful for fixed per-user configuration by combining it with a [`Map`](/aztec-nr-api/mainnet/noir_aztec/state_vars/struct.Map.md), e.g. a registry of their account types.

`PublicImmutable`'s main limitation is the immutability, which in many cases leads to [`DelayedPublicMutable`](/aztec-nr-api/mainnet/noir_aztec/state_vars/struct.DelayedPublicMutable.md) being used instead. But in those cases where fixed values are not a problem, this is a fine choice for storage.

## Examples

Declaring a `PublicImmutable` in the contract's [`storage`](/aztec-nr-api/mainnet/noir_aztec/macros/storage/fn.storage.md) struct requires specifying the type `T` that is stored in the variable:

```rust
#[storage]
struct Storage<C> {
    decimals: PublicImmutable<u8, C>,

    account_types: Map<AztecAddress, PublicImmutable<AccountType, C>, C>,
}
```

## Requirements

The type `T` stored in the `PublicImmutable` must implement the [`Packable`](/aztec-nr-api/mainnet/protocol_types/traits/trait.Packable.md) trait.

## Implementation Details

Values are packed and stored directly in the public storage tree, along with the hash of the packed representation. A `PublicImmutable` therefore takes up as many storage slots as the packing length of the stored type `T`, plus one. This hash allows for efficient private reads, such that only a single public storage value is read. For more details, see [`WithHash`](/aztec-nr-api/mainnet/noir_aztec/utils/struct.WithHash.md).

An initialization nullifier prevents re-initialization of the `PublicImmutable`. This allows reading an initialized `PublicImmutable` from a private contract function, since the value is guaranteed to not change.

Private contract functions however **cannot** determine that a `PublicImmutable` has not been initialized, as they do not have access to the current network state, only the *past* state at the anchor block. They *can* perform historical non-inclusion proofs of the initialization nullifier at past times, but they have no way to guarantee that it has not been emitted since then.

A local cache for the immutable value reduces node roundtrips.

## Implementations

```rust
impl<T, Context> PublicImmutable<T, Context>
```

```rust
pub fn compute_initialization_inner_nullifier(self) -> Field
```

Returns the inner nullifier emitted during initialization.

```rust
impl<T> PublicImmutable<T, PublicContext>
```

```rust
pub fn initialize(self, value: T)
where
    T: Packable
```

Stores a permanent value.

This function can only be called once on a given `PublicImmutable`: subsequent calls will fail with a duplicate nullifier.

#### Examples

Contract initialization:

```rust
#[external("public")]
#[initializer]
fn initialize(decimals: u8) {
    self.storage.decimals.initialize(decimals);
}
```

Non-initializer initialization:

```rust
// Can only be called once per account
#[external("public")]
fn set_account_type(account_type: AccountType) {
    self.storage.account_types.at(self.msg_sender()).initialize(account_type);
}
```

#### Cost

The `SSTORE` AVM opcode is invoked a number of times equal to `T`'s packed length plus one, and the `EMITNULLIFIER` AVM opcode is invoked once.

```rust
pub fn read(self) -> T
where
    T: Packable
```

Reads the permanent value.

This function reverts the transaction if the `PublicImmutable` is not initialized.

#### Examples

A public getter that returns the permanent value:

```rust
#[external("public")]
fn get_decimals() -> u8 {
    self.storage.decimals.read()
}
```

#### Cost

The `SLOAD` AVM opcode is invoked a number of times equal to `T`'s packed length, and the `NULLIFIEREXISTS` AVM opcode is invoked once.

```rust
pub fn read_unsafe(self) -> T
where
    T: Packable
```

Reads the permanent value, skipping the initialization check.

This is cheaper than [`PublicImmutable::read`](/aztec-nr-api/mainnet/noir_aztec/state_vars/struct.PublicImmutable.md#read), but it should only be used if the `PublicImmutable` is known to be initialized.

If the `PublicImmutable` is not initialized, this returns the default empty public storage value, which is all zeroes - equivalent to `let t = T::unpack(std::mem::zeroed());`.

#### Examples

A public getter that returns the permanent value:

```rust
#[external("public")]
fn get_decimals() -> u8 {
    // This call is safe because `decimals` is initialized in the contract's initializer function
    self.storage.decimals.read_unsafe()
}
```

#### Cost

The `SLOAD` AVM opcode is invoked a number of times equal to `T`'s packed length.

```rust
pub fn is_initialized(self) -> bool
```

Returns `true` if the `PublicImmutable` has been initialized.

#### Examples:

Conditional initialization:

```rust
#[external("public")]
fn set_account_type_if_not_set(account_type: AccountType) {
    if !self.storage.account_types.at(self.msg_sender()).is_initialized() {
        self.storage.account_types.at(self.msg_sender()).initialize(account_type);
    }
}
```

#### Cost

The `NULLIFIEREXISTS` AVM opcode is invoked once.

```rust
impl<T> PublicImmutable<T, UtilityContext>
```

```rust
pub unconstrained fn read(self) -> T
where
    T: Packable
```

Reads the permanent value.

This function throws if the `PublicImmutable` is not initialized.

#### Examples

```rust
#[external("utility")]
fn get_decimals() -> u8 {
    self.storage.decimals.read()
}
```

```rust
pub unconstrained fn is_initialized(self) -> bool
```

Returns `true` if the `PublicImmutable` has been initialized.

```rust
impl<T> PublicImmutable<T, &mut PrivateContext>
```

```rust
pub fn read(self) -> T
where
    T: Packable
```

Reads the permanent value.

This function throws if the `PublicImmutable` is not initialized.

#### Examples

```rust
#[external("private")]
fn get_decimals() -> u8 {
    self.storage.decimals.read()
}
```

#### Cost

A nullifier existence request is pushed to the context, which will be verified by the kernel circuit. Additionally, a historical public storage read at the anchor block (which is on the order of 4k gates) is performed for a single storage slot, **regardless of `T`'s packed length**. This is because [`PublicImmutable::initialize`](/aztec-nr-api/mainnet/noir_aztec/state_vars/struct.PublicImmutable.md#initialize) stores not just the value but also its hash: this function obtains the preimage from an oracle and proves that it matches the hash from public storage.

Because of this reason it is convenient to group together all of a contract's public immutable values that are read privately in a single type `T`:

```rust
// Bad: reading both `decimals` and `symbol` will require two historical public storage reads
#[storage]
struct Storage<Context> {
    decimals: PublicImmutable<u8, Context>,
    symbol: PublicImmutable<FieldCompressedString, Context>,
}

// Good: both `decimals` and `symbol` are retrieved in a single historical public storage read
#[derive(Packable)]
struct Config {
    decimals: u8,
    symbol: FieldCompressedString,
}

#[storage]
struct Storage<Context> {
    config: PublicImmutable<Config, Context>,
}
```

## Trait implementations

```rust
impl<Context, let M: u32, T> StateVariable<M + 1, Context> for PublicImmutable<T, Context>
where
    T: Packable<N = M>
```

```rust
pub fn new(context: Context, storage_slot: Field) -> Self
```

```rust
pub fn get_storage_slot(self) -> Field
```
