Rust for Solana Basics
10 examples to get you started with Rust On-Chain - 7 basic and 3 intermediate.
Search across all documentation pages
10 examples to get you started with Rust On-Chain - 7 basic and 3 intermediate.
cargo and the Solana CLI 3.0.10 toolchain (cargo build-sbf).solana-program (native) or Anchor 0.32.1.solana-test-validator for integration tests.Every on-chain program exposes a single entrypoint! that receives accounts and instruction data.
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
pubkey::Pubkey,
};
entrypoint!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
Ok(())
}process_instruction for every instruction in a transaction.Ok(()) on success; return Err(ProgramError::...) to fail the transaction.main function - the BPF/SBF loader invokes your entrypoint directly.Related: The Entrypoint & Dispatch - routing instructions
On-chain crates compile without the standard library.
#![no_std]
extern crate alloc;
use alloc::vec::Vec;#![no_std] disables std; use alloc for Vec, String, and Box when needed.solana_program.Related: no_std & the Program Environment - available APIs
Most account layouts use Borsh for deterministic byte encoding.
use borsh::{BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize)]
pub struct Counter {
pub count: u64,
}
let mut data = account.try_borrow_mut_data()?;
counter.serialize(&mut &mut data[..])?;Related: Borsh Serialization - full guide
Pubkeys are 32-byte identifiers for accounts and programs.
use solana_program::pubkey::Pubkey;
let key = Pubkey::new_from_array(account.key.to_bytes());
if *account.key != expected_owner {
return Err(ProgramError::IncorrectProgramId);
}Pubkey implements Copy and compares in constant time - use it for owner checks.Overflow panics abort the transaction - use checked math.
let new_balance = old_balance
.checked_add(amount)
.ok_or(ProgramError::InvalidArgument)?;checked_*.saturating_* is appropriate when capping values is acceptable.+, -, or *.Related: Safe Arithmetic - overflow patterns
Fail fast with typed errors instead of panicking.
use solana_program::program_error::ProgramError;
if amount == 0 {
return Err(ProgramError::InvalidArgument);
}ProgramError codes are compact and cheap compared to custom strings.thiserror or manual) map to u32 codes for clients.unwrap() or expect() in production program paths.Related: Error Handling On-Chain - custom errors
Account data is accessed through RefCell-style borrows.
let data = account.try_borrow_data()?;
if data.len() < Counter::LEN {
return Err(ProgramError::InvalidAccountData);
}try_borrow_data fails if the account is already mutably borrowed.data.len() matches your struct size before parsing.Large accounts can use bytemuck to reinterpret bytes without copying.
use bytemuck::{Pod, Zeroable};
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct Header {
pub discriminator: u64,
pub count: u64,
}
let header = bytemuck::from_bytes::<Header>(&data[..8]);Pod + Zeroable require #[repr(C)] and no padding surprises.Related: Zero-Copy with bytemuck - Pod rules
Use msg! sparingly; logs cost compute units.
solana_program::msg!("deposit: {} lamports", amount);Related: Logging with msg! - CU-aware logging
Bound iterations and avoid heap growth in hot paths.
const MAX_ITEMS: usize = 32;
for (i, item) in items.iter().take(MAX_ITEMS).enumerate() {
process_item(i, item)?;
}solana program show --programs and transaction simulation.Related: Program Size & CU Discipline - optimization
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.
Reviewed by Chris St. John·Last updated Jul 16, 2026