Rust is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety. As a beginner, practical code examples are crucial for grasping new concepts and building real-world applications. This comprehensive guide provides over 20 rust code examples for common programming tasks.
1. Hello World
The "Hello World" program is the simplest way to ensure Rust is installed and working properly:
fn main() {
println!("Hello World!");
}
This program uses the println!
macro to print a string to standard output. The main
function is the entry point for execution.
2. Read User Input
Reading input from a user is done with the stdin()
method of the io::stdin()
function:
use std::io;
fn main() {
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read line");
println!("You entered: {}", input);
}
This reads a single line into a mutable input
string. The expect
call handles errors.
3. Format Print Output
Rust provides various macros to format text output:
fn main() {
let app_name = "Rust Blog";
let downloads = 1_024;
println!("Welcome to {}!", app_name);
println!("Downloads: {}", downloads);
println!("Version: {:.1}", 1.2);
}
println!
works like C‘s printf
, formatting variables into the string. The :{.precision}
specifies a floating point precision.
4. If Conditional
An if
statement allows executing code selectively based on boolean conditions:
fn main() {
let admin = true;
if admin == true {
println!("You have admin rights!");
} else {
println!("You do not have admin rights.");
}
}
if
statements support else if
and else
blocks. Variables must be explicitly compared against a boolean with == true
or == false
.
5. Match Conditional
A match
statement works like a switch
from other languages:
fn get_department(id: u32) -> &‘static str {
match id {
1 => "Engineering",
2 => "Finance",
3 => "Marketing",
_ => "Unknown"
}
}
fn main() {
let department = get_department(3);
println!("Matched: {}", department);
}
The _
catch-all pattern handles non-matching cases.
6. While Loop
A while
loop repeats code as long as a condition is true:
fn main() {
let mut counter = 1;
while counter < 11 {
println!("Counter: {}", counter);
counter += 1;
}
}
The loop increments counter
each iteration until the value reaches 10. mut
allows modifying the variable.
7. For Range Loop
For loops iterate a specific number of times with a range:
fn main() {
for i in 1..11 {
println!("Number: {}", i);
}
}
This loops from 1 to 10, excluding the end number. The range could also count downwards with 10..=0
.
8. Loop Over Collections
You can iterate reference elements from a vector with for
:
fn main() {
let names = vec!["Bob", "Anne", "Steve"];
for name in &names {
println!("Hello {}!", name);
}
}
Looping over &names
borrows references to the elements rather than moving them.
9. Loop and Modify Collection
To loop and modify a collection, make it mutable:
fn main() {
let mut scores = [10, 20 , 30];
for score in &mut scores {
*score += 5;
}
println!("Scores: {:?}", scores);
}
Now *score
dereferences each element for modification.
10. Handle Errors
Errors are handled via Result
enums with pattern matching:
use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
let number = "10a";
let n: i8 = match number.trim().parse() {
Ok(n) => n,
Err(e) => return Err(e),
};
println!("Number: {}", n);
Ok(())
}
The ?
operator is also used for concise error propagation.
11. Vector
A vector stores a growable list of elements:
fn main() {
// Construct empty vector
let mut values = Vec::new();
// Add elements
values.push(1);
values.push(3);
// Show elements
println!("Values: {:?}", values);
}
Vectors dynamically grow and shrink as elements are added and removed.
12. Vector Loop
Loop through vector values with a for
loop:
fn main() {
let mut values = vec![1, 2, 3];
// Loop and modify
for value in &mut values {
*value += 1;
}
println!("Values: {:?}", values);
}
Borrowing vector elements immutably would prevent modifications.
13. Vector Sort
Sort a vector with the sort
method:
fn main() {
let mut values = vec![3, 1, 2];
// Sort vector
values.sort();
println!("Sorted: {:?}", values);
}
For custom sorting behavior, pass a closure comparator function to sort_by
.
14. HashMap
A hash map stores key-value pairs:
use std::collections::HashMap;
fn main() {
let mut players = HashMap::new();
players.insert("Russell", 13);
players.insert("Wilt", 100);
println!("{:?}", players);
}
Map elements are automatically sorted by key. get
and contains_key
find values.
15. HashMap Loop
You can loop through key-value pairs in a map:
use std::collections::HashMap;
fn main() {
let players = hashmap!{"Russell" => 13, "Wilt" => 100};
for (name, number) in &players {
println!("{} wore number {}", name, number);
}
}
Each iteration provides a tuple with both elements.
16. Modules
Code can be organized into modules:
// In file arithmetic.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
// In main.rs
mod arithmetic;
fn main() {
let sum = arithmetic::add(5, 2);
println!("Sum: {}", sum);
}
The module file path matches the name, like arithmetic.rs
. Declare public functions with pub
.
17. Structs
Custom data structures are defined using struct
:
struct Book {
title: String,
chapters: u8,
author: String,
}
fn main() {
let book = Book {
title: String::from("Frankenstein"),
chapters: 24,
author: String::from("Mary Shelley"),
};
println!("{} by {} has {} chapters",
book.title, book.author, book.chapters);
}
Struct fields are accessed using dot notation. The struct is instantiated like an object.
18. Impl Methods
Methods are added to structs with impl
:
struct Circle {
radius: f64
}
impl Circle {
// Associated function
fn new(radius: f64) -> Self {
Self { radius }
}
// Instance method
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
fn main() {
let c = Circle::new(5.0);
println!("Area: {}", c.area());
}
Self
refers to the struct type. Methods take a struct reference with &self
.
19. Traits
Traits define shared behavior between types:
trait Summary {
// Mandatory method
fn summarize(&self) -> String;
}
struct Book {
// ...
}
// Implement trait for type
impl Summary for Book {
fn summarize(&self) -> String {
format!("{} by {}", self.title, self.author)
}
}
fn main() {
let book = Book { /* ... */ };
println!("Summary: {}", book.summarize());
}
Traits are similar to interfaces in other languages. Types like Book
can implement them.
20. Generic Structs
Structs can be made generic over one or more type parameters:
struct Point<T> {
x: T,
y: T,
}
fn main() {
let p1 = Point{x: 5, y: 10};
let p2 = Point{x: 5.5, y: 8.2};
println!("P1 = ({}, {})", p1.x, p1.y);
println!("P2 = ({}, {})", p2.x, p2.y);
}
Now Point
supports any data types, not just floats/integers.
21. Threads
Concurrency is achieved via threads and message passing:
use std::thread;
fn main() {
// Spawn thread in the background
thread::spawn(|| {
println!("...printing from spawned thread");
});
// Main thread continues execution
println!("...printing from main thread");
}
The closure passed to spawn
executes concurrently in the new thread. Accessing data must be synchronized manually between threads.
22. Channels
The transmission of data between threads uses channels:
use std::sync::mpsc::channel;
use std::thread;
fn main() {
let (tx, rx) = channel();
for id in 0..5 {
let tx = tx.clone();
thread::spawn(move || {
let msg = format!("Message {}", id);
tx.send(msg).unwrap();
});
}
for msg in rx {
println!("Got {}", msg);
}
}
The tx
handle sends data. rx
receives in the main thread. Channels manage safe simultaneous data transmission.
23. Read File
Reading a file loads the contents into memory:
use std::fs;
use std::io::Read;
fn main() -> Result<(), std::io::Error> {
let mut text = String::new();
// Open file
let mut f = fs::File::open("notes.txt")?;
f.read_to_string(&mut text)?;
println!("File contents:\n{}", text);
Ok(())
}
The question mark operator passes errors up the call stack for handling.
24. Write File
To write text to a file:
use std::fs::File;
use std::io::{self, Write};
fn main() -> Result<(), io::Error> {
let text = String::from("\nLorem Ipsum ...");
let mut file = File::create("lorem.txt")?;
file.write_all(text.as_bytes())?;
Ok(())
}
The write_all
method accepts a byte slice. Files are automatically flushed and closed at the end of scope.
Conclusion
Rust‘s design prevents entire classes of bugs while providing great performance. These 24 code samples demonstrate how to use Rust for common tasks like I/O, collections, threading, and more. With this solid base, you can start building real programs in Rust! Let me know if you have any other questions.