Developers looking to get into Solana development who already know Rust have a great head start. Rust is an officially supported language for writing on-chain programs for Solana. However, several key differences in the language's usage could otherwise be confusing.
This article will walk through several of those differences, specifically the setup details, restrictions, macro changes, and compute limits. Additionally, this article will cover the development environments and frameworks needed to start with Solana.
By the end of this article, Rust developers will understand the differences they need to know to start their Solana journeys.
Understanding the Core Differences
First, note that this article aims at understanding the differences in using Rust as a language when working with Solana. It won’t cover Blockchain or Solana basics.
It also won’t cover core Solana concepts that must be understood in order to program in Solana, such as:
- Programs - Solana’s version of smart contracts
- Accounts - A record in the Solana ledger that either holds data (a data account) or is an executable program
- Various fees - Such as base fee, priority fee, and rent
- Transactions - Interactions with the network that contain instructions, signatures, and more.
For more information on those core concepts, check out the Solana developer documentation.
Let’s now look at the differences in project setup.
Key Setup Details
On-chain programs for Solana in Rust are still Rust programs at heart. They still follow the standard Rust project with a /src
folder and Cargo.toml
file in the root. That said, there are several key differences.
Project Dependencies
To get started, the solana-program crate is required for every on-chain Solana program written with Rust. This is the base library for all on-chain Rust programs. The library defines macros for the required program entrypoint (see below), core data types, logging macros, and more.
Program Entrypoint
Instead of a main
function, Solana programs use the entrypoint!
macro. This symbol is exported and then called by the Solana runtime when the program runs. The entrypoint macro calls a given function, which must have the following type signature:
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
//program code goes here
}
These three parameters are passed to every on-chain program.
- The
program_id
is the public key of the current program. - The
accounts
are all accounts that are required to process the instruction. - The
instruction_data
is data specific to that instruction.
Every program must then call the entrypoint! macro on the instruction, as follows:
entrypoint!(process_instruction);
Building and Testing
After installing the Solana command-line tools, projects can be built to target host machines as normal with cargo build
.
However, to target the Solana runtime, use cargo build-bpf
or cargo build-spf
which will compile the program to the bytecode necessary to run it on the Solana runtime.
Unit testing can be achieved via cargo test
with standard #test
attributes. For more integrated testing, the solana-program-test crate provides a local Solana runtime instance which can be used in conjunction with outside tests sending transactions.
Finally, a full test cluster can be started with the solana-test-validator, installed along with the Solana CLI. This creates a fully featured test cluster on a local machine, which can then deploy programs and run tests.
Understanding Restrictions
While most standard Rust crates are available in the Solana runtime, and third-party crates are supported as well, there are several limitations. Since the Solana runtime has resource constraints and must run deterministically, here are the differences to be aware of:
Package Limitations
The following packages are unavailable:
- rand
- std::fs
- std::net
- std::future
- std::process
- std::sync
- std::task
- std::thread
- std::time
The following packages have limited functionality:
- std::hash
- std::os
Rand Dependencies
Because programs must run deterministically, the rand
crate is not available. Using an additional crate that depends on rand
will also cause compile errors.
However, if the crate used simply depends on rand
but does not actually generate random numbers, then it is possible to work around this by adding the following to the program’s Cargo.toml:
getrandom = { version = "0.1.14", features = ["dummy"] }
Macro Changes
Several standard macros have been replaced or have modified behavior to be aware of. First, the println!
has been replaced with the computationally simpler msg!
macro. The msg!
macro outputs to the program logs, and it can be used as follows:
msg!(“Your message”);
msg!(0_64, 1_64, 2_64);
msg!(“Your variable: {:?}”, variable);
The panic!, assert!
, and any internal panics are also output to the program logs by default. However, this can be modified with a custom panic handler.
Compute Budget
As a Rust developer, efficient computing is nothing new. What may be different is that in Solana, each transaction has a fixed compute budget that it must not surpass. When transactions exceed the compute budget, they are halted and return an error.
Programs can access the number of remaining compute units via the sol_remaining_compute_units
system call, and can log the remaining number of compute units with sol_log_compute_units
.
Learning the Development Environments and Frameworks
While the Solana CLI and the solana_program
crate are all that is needed to get started, there are a couple of helpful tools which can accelerate learning.
Solana Playground
The Solana Playground is a browser-based IDE that allows developers to develop and deploy Solana programs.
It’s the easiest way to begin developing with Solana, and it supports building, testing, and deploying Solana Rust programs. Additionally, a number of built-in tutorials are available to guide learning.
Using Anchor
Anchor is a framework that seeks to accelerate the building of secure Solana programs. It can help by handling standard boilerplate code, speeding up the development cycle. Additionally, it provides some security checks by default, making Solana programs more secure.
To create a new program, simply create a new Anchor project in the Solana playground:
Alternatively, install the Anchor CLI locally, and then use anchor init <project-name>
to create a new Anchor project.
Creating Off-chain Programs
So far, this article has covered the key details of developing on-chain Solana programs in Rust. However, it’s also possible to develop off-chain Solana clients in Rust. This can be done by using the solana_sdk crate. This contains the solana_client crate that allows Rust programs to interact with a Solana node via the JSON RPC API.
Another option is to use the anchor_client crate which interacts with Solana programs written in Anchor via RPC. Alternatively, consider writing on-chain programs in Rust, and off-chain clients in JS/TS.
Wrap-up
This article has covered the basics of developing for Solana with Rust, from setup details and restrictions to development environments and frameworks. Ready to put all that into practice? Check out this guide for Rust developers writing their first Solana program.
For more Rust-related Solana resources, check out the Developing with Rust page. And for other Solana program examples written with Rust, check out these examples on GitHub.