Learning Rust: My First Steps with HackQuest’s Eclipse Track


So, I recently signed up for the HackQuest Eclipse track, and that’s when I decided to take the plunge and start learning Rust.
At first? Smooth sailing! The theoretical part made sense, and I was like, “Okay, I got this.”
Then... came the technical part gosh and suddenly,
“What the hell?!”
became my daily catchphrase 😅😂
What Wasn’t Too Bad:
Not everything was scary, though. Some things were pretty digestible early on:
Rust’s syntax (it's actually pretty neat)
Basic data types
Compiling and building the project
Setting up the project root directory
Using comments
Printing outputs
Declaring variables
At that point, I was still doing okay ,feeling confident, even.
Then Came the Deep Technical Stuff:
Now this is where the real test began.
Advanced data types
Modules
Ownership, borrowing, lifetimes... oh my 😩
Trust me, without a solid grasp of these, things fall apart fast. Especially if you’re coming from a language where memory management isn’t something you think about all the time.
📌 Quick Side Note on Strings in Rust:
Starting off with datatypes:
- strings
Rust has two kinds of strings, and it took me a second to really get it:
String
→ Owned (Heap-allocated, mutable)&str
→ Borrowed (Immutable reference to a string slice)
The only visual clue?
One starts with a capital letter (String
) and the other with a&
symbol (&str
)which actually tells you a lot about how ownership and borrowing work in Rust.So the syntax?
here we go ……
//String
let you_name:String=”essie is my name”.to_string(); //similar to
let you_name:String=String::from(“essie is my name”");
//&string
let name_literal: &str = "Essie"; // this is string slice(its a string that can only be accessed but not modified)
let name_owned: String = String::from("Essie"); // owned String
Integers & Floating-Point Numbers in Rust
Rust provides two main categories of numeric types: integers and floating-point numbers.
Integers are whole numbers (positive, negative, or zero) like
i32
,u8
,i64
(side note:the i,u rep—> signed and unsigned types=signed(i) can store both positive and negative values the other(u) can only store positive values, including zero)Floating-point numbers are used to represent numbers with decimal points, such as
3.14
or-0.5
.
Rust supports two floating-point types:
f32
— 32-bit floating-pointf64
— 64-bit floating-point (default)
📌 Note: The number (e.g., 32 or 64) refers to the bit precision, not your CPU type. While
f64
is the default and generally preferred for higher precision, Rust doesn’t automatically choose based on your CPU—it’s about performance and accuracy trade-offs.
Modules in Rust (Yes, They're a Thing!)
At first, I thought "Rust doesn’t really have modules like Python" LOL.
Well, plot twist: it does. They're just called modules, and they work a bit differently from what you might be used to in Python.
What Are Modules in Rust?
theyre just like a function(reausaable code) and are more about organizing and structuring your code but declared differently in contrast to functions with the keyword mod and curly braces.
Think of a module as a container for related functions, structs, enums, and other items. It helps you break your code into logical, manageable parts.
🔑 Key Points:
Declared using the
mod
keyword.Use curly braces
{}
to define their scope.Not automatically public —>you must use
pub
to expose contents.Can be nested or split across files for better organization.
mod myfirst_module{ // // 'myfirst_module' is the module name pub myfunct(){//'myfunct' is a function defined inside 'myfirst_module' println!("Hello from the greetings module!"); } }
//You can then call it from main() like this:
fn main(){
myfirst_module::myfunct(); // myfirst_module is a module then :: accesses items inside the function myfunc
//'::' is the path operator used to access items (like functions) inside the module
}
📌 Don’t forget the pub
keyword without it, myfunct()
is private and won’t be accessible from main()
or anywhere else.
If you were importing a module from a different crate (like a library you installed via Cargo.toml
), then you'd use:
use other_crate::some_module;
before you can use a module or function from another crate (external library) in your Rust project, you first need to add the crate to your Cargo.toml
file.
How to Use an External Crate in Rust
📝 Step 1: Add the crate to Cargo.toml
Let’s say you want to use the popular rand
crate for generating random numbers.
In your Cargo.toml
:
[dependencies]
rand = "0.8" # or whatever the latest version is .his tells Cargo to download and compile the crate from crates.io the next time you build your project.
Step 2: Using the crate in your Rust file
In your main.rs
:
use rand::Rng; // Rng is a trait inside the rand crate
fn main() {
let mut rng = rand::thread_rng();
let n: u8 = rng.gen_range(1..=100);
println!("Random number: {}", n);
}
bring the whole crate of rand vs bringing the only triats you need?
Bring only what you need into scope is considered best practice, and here’s why:
Bringing the whole crate
use rand;
fn main() {
let mut rng = rand::thread_rng();
let n: u8 = rand::Rng::gen_range(&mut rng, 1..=10);
}
Bringing only the trait you need
use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
let n: u8 = rng.gen_range(1..=10);
}
✅ TL;DR – Why You Should Only Import What You Need in Rust
✅ 1. Clarity & Readability
Using something like use rand::Rng;
makes it immediately clear what you're working with. No guesswork, it’s explicit and self-documenting.
✅ 2. Avoid Namespace Pollution
Importing the entire crate (use rand;
) brings in everything ;even things you don’t need. This can lead to name collisions and clutter, especially in large or multi-crate projects.
✅ 3. Better Autocompletion & Docs
IDE tools like VS Code and IntelliJ work better with targeted imports. You’ll get cleaner autocomplete, better error messages, and easier-to-follow documentation.
✅ 4. It’s Idiomatic Rust
Minimal, precise imports are part of what makes Rust code clean and maintainable. It reflects Rust’s core values: explicitness, modularity, and fine-grained control.
✨ Bottom line: Import what you use ; and nothing more.
📚 Common Rust Crates & Their Traits
Crate Purpose Notable Traits rand Random number generation Rng
,SeedableRng
serde Serialization/deserialization Serialize
,Deserialize
tokio Async runtime AsyncRead
,AsyncWrite
,Stream
futures Async programming primitives Future
,Stream
regex Regular expressions Regex
(struct with methods)chrono Date and time handling TimeZone
,DateTime
(structs & traits)🔍 What Are Traits?
Traits in Rust define shared behavior that types can implement.
example
What is the
Deserialize
Trait?The
Deserialize
trait comes from theserd``e
crate (short for Serialize/Deserialize).
It allows Rust structs and enums to be converted from data formats like JSON into usable Rust types.First: Add
serde
andserde_json
to yourCargo.toml
[dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0"
Using Deserialize
use serde::Deserialize;
Step 2: Create a struct and derive Deserialize
#[derive(Debug, Deserialize)]
struct User {
name: String,
age: u8,
}
Step 3: Deserialize JSON into the struct
use serde_json;
fn main() {
let data = r#"{"name": "Essie", "age": 22}"#;
let parsed: User = serde_json::from_str(data).unwrap();
println!("Name: {}, Age: {}", parsed.name, parsed.age);
}
TL;DR – What Deserialize
Does
Trait | Purpose |
Deserialize | Converts external data → Rust |
📥 Takes formats like JSON, TOML, YAML
🧱 Maps them to Rust structs/enums
⚙ Requires the struct to
#[derive(Deserialize)]
💡 Bonus
You can use serde::Serialize
together with Deserialize
to do both directions:
rustCopyEdit#[derive(Serialize, Deserialize)]
struct User { ... }
If you're diving into Solana development using Rust, you're entering one of the most advanced and exciting use cases for the language. Solana smart contracts (called programs) are written in Rust, and they require mastery over some core Rust modules and Solana-specific crates.
Here’s a list of must-know Rust modules & concepts to get you started on the right foot:
🦀 Must-Know Rust Modules & Crates for Solana Development
🔧 1. std::vec
, std::string
, std::collections
These are foundational.
You'll work with
Vec<u8>
,String
, and sometimesHashMap
.
Why? Solana programs work with binary data (Vec<u8>
) and serialized inputs.
🗃️ 2. borsh
or serde
(for serialization/deserialization)
- Solana programs often require you to serialize/deserialize instruction data or account state.
tomlCopyEditborsh = "0.10"
serde = { version = "1.0", features = ["derive"] }
Traits to know:
BorshSerialize
,BorshDeserialize
Serialize
,Deserialize
🧱 3. solana_program
crate
- The official SDK for Solana programs.
tomlCopyEditsolana-program = "1.18" # (match your Solana CLI version)
Includes:
entrypoint!
macro – defines your program's entrypointProgramResult
,AccountInfo
,Pubkey
– core typesmsg!
– logging within Solanainvoke
/invoke_signed
– cross-program invocations
🔐 4. solana_sdk
crate (for local testing or clients)
Used more in clients or tests than inside the program.
Includes keypair generation, transactions, signatures, etc.
📦 5. anyhow
, thiserror
– for cleaner error handling
tomlCopyEditanyhow = "1.0"
thiserror = "1.0"
- Helps create descriptive, debuggable errors instead of
Box<dyn Error>
🧪 6. solana-program-test
Simulates a local Solana cluster for testing your smart contracts off-chain.
Works with
tokio
andasync
code.
🔄 Bonus: Core Rust Concepts You Must Know
Concept | Why It Matters in Solana Dev |
Ownership & Borrowing | Accounts are references—misuse = compile error |
Lifetimes | When using references inside structs/enums |
Enums | For instruction types (like SPL Token program) |
Traits | For serialization, logging, etc. |
Macros | Used in entrypoint! , msg! , etc. |
Result/Error Handling | Must return ProgramResult on failure |
TL;DR?
Cont’' : Rust Modules(some repeated)
These come from the standard library (std
) and are essential for any Rust development especially Solana:
🔹 Foundational Modules
std::vec::Vec
– Dynamic arrays (e.g.Vec<u8>
)std::string::String
– Text datastd::collections::HashMap
– Key-value storagestd::result::Result
– Error handlingstd::option::Option
– Handling nullable valuesstd::convert
– ForInto
,From
, etc.std::mem
– For manipulating memory layoutstd::cmp
– Comparisons (e.g.PartialEq
,Ord
)std::fmt
– Formatting, customDisplay
std::io
– (Used more off-chain, but useful for CLI tools used to get user input )
📦 Solana-Specific Crates
These are the official and community crates you’ll encounter in Solana dev:
🔹 Solana Program Development
solana_program
– Core SDK for on-chain programs- Includes types like
AccountInfo
,Pubkey
,entrypoint!
,invoke
- Includes types like
solana_sdk
– Used for client-side development (keypairs, RPC, etc.)solana_program_test
– Local in-Rust test environment (simulates a cluster)solana_client
– RPC client to interact with Solana from off-chain appssolana_clap_utils
– CLI utilities for building wallets or clientssolana_transaction_status
– Used to fetch and parse tx info from RPC
🔄 Serialization / Deserialization
Solana smart contracts almost always involve data encoding.
borsh
– Binary Object Representation Serializer for Hashing- Use
#[derive(BorshSerialize, BorshDeserialize)]
- Use
serde
– General-purpose serializationserde_json
– Use withserde
for JSON APIs (mostly off-chain)serde_bytes
– Efficient binary serialization viaserde
⚙️ Smart Contract Patterns & Utils
Useful tools that make writing contracts easier.
thiserror
– For clean, enum-based error messagesanyhow
– Flexible error handling (better for off-chain/client-side)num_derive
/num_traits
– For converting between typesbytemuck
– Zero-cost casting for byte slicesarrayref
– For working with byte arrays in account database64
– Encoding/decoding (used in some off-chain RPC workflows)
🧪 Testing & Tooling
tokio
– Async runtime for client-side interactions or testingassert_matches
– For precise assertions in unit testsdotenvy
/dotenv
– Load.env
configs for local dev/testingclap
– Build CLI tools that interact with Solana programs
🧩 Bonus: Optional But Helpful
spl_token
– Solana Program Library's token program bindingssolana-metadata
– Used in NFT developmentanchor_lang
– If you're using the Anchor framework (high-level abstraction over Solana)
✅ TL;DR Summary Table
Category | Key Crates or Modules |
Core Rust | Vec , String , Result , Option , fmt |
Solana Core | solana_program , solana_sdk , borsh |
Serialization | borsh , serde , serde_json |
Testing/Clients | tokio , solana_program_test , dotenvy |
Utils | thiserror , arrayref , num_traits |
Learning Rust for Solana development is a journey filled with challenges, breakthroughs, and those priceless “aha!” moments and I’d genuinely love to hear about yours too.
Whether you're just getting started or deep into building on-chain programs, your experience might help someone else take the next step.
🗣️ Drop your thoughts, questions, or even frustrations in the comments let’s learn together!
P.S. If you’re brand new to Rust, I recommend starting with the W3Schools Rust Introduction it's beginner-friendly and a great way to grasp the fundamentals quickly before diving deeper.
Subscribe to my newsletter
Read articles from ESTHER NAISIMOI directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
