Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Records

Records are named product types (structs) that group related fields together. They are declared inside an entity and can be used as member types, route parameters, and intermediate values.

Declaration

A record is declared with the record keyword followed by named, typed fields:

entity Marketplace {
    record Listing {
        seller: address,
        price: U256,
        active: bool
    }

    record Bid {
        bidder: address,
        amount: U256,
        timestamp: u64
    }
}

Each field has a name and a type, separated by a colon. Fields are separated by commas.

Construction

Records are constructed by providing values for all fields:

let listing = Listing {
    seller: msg::sender,
    price: 1000,
    active: true
};

Every field must be specified – there is no default value mechanism. The order of fields in the constructor does not need to match the declaration order, but all fields must be present.

Field Access

Fields are accessed with dot notation:

let price = listing.price;
let who = listing.seller;

if listing.active {
    // handle active listing
}

Functional Update

Cambrian supports a functional update syntax that creates a new record value with some fields changed while keeping the rest:

let updated = listing { active: false };

This produces a new Listing with active set to false and seller and price carried over from the original. The original listing is not mutated.

Functional update is especially useful in member transforms, where you need to produce a new state value based on the current one:

m_listing: Listing {
    in create(seller, price) => Listing {
        seller: seller,
        price: price,
        active: true
    }
    in deactivate() => m_listing { active: false }
    in update_price(new_price) => m_listing { price: new_price }
}

You can update multiple fields at once:

let revised = listing {
    price: new_price,
    active: true
};

Records as Member Types

Records are commonly used as the types of members to group related state:

entity Escrow {
    record Deal {
        buyer: address,
        seller: address,
        amount: U256,
        released: bool
    }

    routes {
        init create(buyer: address, seller: address, amount: U256) => []

        release()
            from Buyer(_)
            where m_deal.released == false : throw 101
        => []
    }

    m_deal: Deal {
        in create(buyer, seller, amount) => Deal {
            buyer: buyer,
            seller: seller,
            amount: amount,
            released: false
        }
        in release() => m_deal { released: true }
    }
}

Records in Collections

Records can be stored in generic collections:

m_bids: Vec<Bid> {
    in place_bid(amount) => m_bids.push(Bid {
        bidder: msg::sender,
        amount: amount,
        timestamp: sys::now
    })
}

Nesting

Records can contain other records as field types:

record Config {
    fee_rate: u64,
    limits: Limits
}

record Limits {
    max_deposit: U256,
    min_deposit: U256
}

Access nested fields with chained dot notation:

let max = config.limits.max_deposit;