Skip to main content
Version: 0.13

Custom Types

When public component methods use custom structs or enums, those types must be annotated with #[export_type] so the compiler can generate corresponding WIT interface definitions. Types used only internally (in private methods or local variables) don't need this annotation.

#[export_type] requirement

Any custom type that appears in a public method signature must be annotated with #[export_type]:

use miden::{export_type, Felt, Word, Asset};

#[export_type]
pub struct StructA {
pub foo: Word,
pub asset: Asset,
}

#[export_type]
pub struct StructB {
pub bar: Felt,
pub baz: Felt,
}

Types that are only used internally (in private methods or local variables) don't need the annotation.

Exporting structs

Struct fields must be public and use types that are either SDK types (Felt, Word, Asset, etc.) or themselves marked with #[export_type]:

use miden::{export_type, Felt, Word, Asset, component};

#[export_type]
pub struct StructA {
pub foo: Word,
pub asset: Asset,
}

#[export_type]
pub struct StructB {
pub bar: Felt,
pub baz: Felt,
}

#[component]
struct MyAccount;

#[component]
impl MyAccount {
pub fn test_custom_types(&self, a: StructA, asset: Asset) -> StructB {
StructB {
bar: a.foo[0],
baz: a.foo[1],
}
}
}

Exporting enums

Enums use the same annotation:

use miden::{export_type, Felt};

#[export_type]
pub enum EnumA {
VariantA,
VariantB,
}

Enum variants can be unit variants (as shown above). The compiler generates the corresponding WIT enum definition.

Types in submodules

Custom types can be defined in submodules. Each type still needs #[export_type]:

pub mod my_types {
use miden::{Felt, export_type};

#[export_type]
pub enum EnumA {
VariantA,
VariantB,
}

#[export_type]
pub struct StructC {
pub inner1: Felt,
pub inner2: Felt,
}
}

// Use in component methods
#[component]
impl MyAccount {
pub fn process(&self, a: StructA, asset: Asset) -> my_types::StructC {
my_types::StructC {
inner1: a.foo[0],
inner2: a.foo[1],
}
}
}

Nested types

Exported types can reference other exported types:

#[export_type]
pub struct LaterDefined {
pub value: Felt,
}

#[export_type]
pub struct ForwardHolder {
pub nested: LaterDefined,
}

The compiler resolves references regardless of declaration order — ForwardHolder can reference LaterDefined even if it's defined first.

Complete example

From the compiler test suite (component-macros-account):

src/lib.rs
#![no_std]
#![feature(alloc_error_handler)]

use miden::{Asset, Felt, Word, component, export_type};

pub mod my_types {
use miden::{Felt, export_type};

#[export_type]
pub enum EnumA {
VariantA,
VariantB,
}

#[export_type]
pub struct StructC {
pub inner1: Felt,
pub inner2: Felt,
}
}

#[export_type]
pub struct StructA {
pub foo: Word,
pub asset: Asset,
}

#[export_type]
pub struct StructB {
pub bar: Felt,
pub baz: Felt,
}

#[component]
struct MyAccount;

#[component]
impl MyAccount {
pub fn test_custom_types(&self, a: StructA, asset: Asset) -> StructB {
StructB {
bar: a.foo[0],
baz: a.foo[1],
}
}

pub fn test_custom_types2(&self, a: StructA, asset: Asset) -> my_types::StructC {
my_types::StructC {
inner1: a.foo[0],
inner2: a.foo[1],
}
}
}
Cargo.toml
[package]
name = "component_macros_account"
version = "0.1.0"
edition = "2024"

[lib]
crate-type = ["cdylib"]

[dependencies]
miden = "0.10"

[package.metadata.component]
package = "miden:component-macros-account"

[package.metadata.miden]
project-kind = "account"
supported-types = ["RegularAccountUpdatableCode"]

Rules summary

RuleDetails
When neededAny custom type in a pub fn signature on a #[component] impl
Struct fieldsMust be pub
Allowed field typesFelt, Word, Asset, AccountId, or other #[export_type] types
EnumsUnit variants supported
ModulesTypes in submodules work — just apply #[export_type] to each
OrderDeclaration order doesn't matter — forward references are resolved

Exported types appear in generated WIT bindings used by cross-component calls.

Full API docs on docs.rs: miden (#[export_type] macro)