20 Notable Rust Features with Examples
Rust is a multi-paradigm, high-level, general-purpose programming language designed for performance and safety, especially safe concurrency. Here are 20 of its key features with illustrative examples:
1. Memory Safety without Garbage Collection
Rust’s borrow checker ensures memory safety at compile time without the need for a garbage collector.
fn main() {
let x = 5;
let y = &x; // Borrowing x
println!("x is {}, y is {}", x, y);
}
Simple borrowing example.
2. Ownership System
Rust manages memory through a system of ownership with rules about borrowing and lifetimes.
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1's ownership is moved to s2
// println!("{}", s1); // This would cause a compile-time error
println!("{}", s2);
}
Ownership move example.
3. Borrowing and Lifetimes
References (borrowing) allow access to data without taking ownership, governed by lifetime annotations to prevent dangling pointers.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
println!("The longest string is {}", result);
}
// println!("The longest string is {}", result); // This would be fine because string1 outlives result
}
Lifetime annotation example.
4. Fearless Concurrency
Rust’s ownership and borrowing rules prevent data races at compile time, enabling safe and easy concurrency.
use std::thread;
use std::sync::Mutex;
use std::rc::Rc;
fn main() {
let counter = Rc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Rc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
Safe concurrency with `Mutex` and `Rc`.
5. Zero-Cost Abstractions
Rust’s features like generics and iterators are designed to provide high-level abstractions without runtime performance overhead.
fn main() {
let v = vec![1, 2, 3];
let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
println!("{:?}", doubled);
}
Zero-cost abstraction with iterators and `map`.
6. Pattern Matching
Rust’s `match` expression provides powerful and concise control flow based on the structure of data.
enum Color {
Red,
Green,
Blue,
Other(String),
}
fn describe_color(color: Color) {
match color {
Color::Red => println!("It's red!"),
Color::Green => println!("It's green!"),
Color::Blue => println!("It's blue!"),
Color::Other(name) => println!("It's some other color: {}", name),
}
}
fn main() {
describe_color(Color::Red);
describe_color(Color::Other(String::from("purple")));
}
Pattern matching with `match` and enums.
7. Traits (Similar to Interfaces or Type Classes)
Traits define shared behavior that types can implement, enabling polymorphism.
trait Summary {
fn summarize(&self) -> String;
}
struct NewsArticle {
headline: String,
location: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {}", self.headline, self.location)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Local government debt"),
location: String::from("Bentonville, AR"),
};
println!("Summary: {}", article.summarize());
}
Traits for defining shared behavior.
8. Generics
Generics allow writing code that works with multiple types without knowing the specific types at compile time.
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for &item in list.iter() {
if item > *largest {
largest = &item;
}
}
largest
}
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
let result = largest(&numbers);
println!("The largest number is {}", result);
let chars = vec!['y', 'm', 'a', 'q'];
let result = largest(&chars);
println!("The largest char is {}", result);
}
Generics for writing type-agnostic code.
9. Macros
Rust’s macro system allows for metaprogramming, enabling code generation at compile time.
macro_rules! say_hello {
() => {
println!("Hello, Rust!");
};
}
fn main() {
say_hello!();
}
A simple declarative macro.
10. Cargo (Build System and Package Manager)
Cargo automates the process of building projects, managing dependencies, and publishing crates (Rust packages).
Cargo simplifies project management and dependency handling.
11. Excellent Error Handling
Rust’s `Result` enum provides a clear and explicit way to handle operations that might fail.
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut file = File::open("hello.txt")?;
let mut s = String::new();
file.read_to_string(&mut s)?;
Ok(s)
}
fn main() {
match read_username_from_file() {
Ok(username) => println!("Username: {}", username),
Err(error) => println!("Error reading username: {:?}", error),
}
}
Error handling with `Result` and the `?` operator.
12. First-Class Support for WebAssembly
Rust has excellent tooling for compiling code to WebAssembly, enabling high-performance web applications.
The `wasm-pack` toolchain simplifies WebAssembly development with Rust.
13. Strong Community and Growing Ecosystem
Rust has a passionate and active community contributing a wide range of libraries (crates) for various domains.
Crates.io is the official package registry for Rust.
14. Pattern Matching with `if let` and `while let`
Syntactic sugar for concise pattern matching in conditional statements and loops.
fn main() {
let optional_number = Some(7);
if let Some(number) = optional_number {
println!("The number is: {}", number);
}
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("Popped: {}", top);
}
}
`if let` and `while let` for concise pattern matching.
15. Closures (Anonymous Functions)
Rust supports anonymous functions that can capture variables from their surrounding scope.
fn main() {
let factor = 2;
let multiply = |n| n * factor;
let result = multiply(5);
println!("Result: {}", result);
}
A simple closure example.
16. Iterators
Rust’s iterators provide a performant and convenient way to process sequences of data.
fn main() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
for val in v1_iter {
println!("Got: {}", val);
}
}
Using iterators to process a vector.
17. Smart Pointers (e.g., `Box`, `Rc`, `Arc`, `RefCell`, `RefMut`)
Smart pointers manage memory and provide different ownership semantics beyond basic references.
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("hello"));
let b = Rc::clone(&a);
let c = Rc::clone(&a);
println!("Reference count of a: {}", Rc::strong_count(&a));
println!("Reference count of b: {}", Rc::strong_count(&b));
println!("Reference count of c: {}", Rc::strong_count(&c));
}
Using `Rc` for shared ownership.
18. Modules and Crates for Code Organization
Rust has a clear system for organizing code into modules within crates (packages).
Modules help manage scope and visibility, while crates are the unit of compilation and distribution.
19. FFI (Foreign Function Interface)
Rust allows interacting with code written in other languages like C.
The `extern` keyword is used to declare functions from external libraries.
20. Performance
Rust is designed for high performance, often comparable to C and C++, due to its low-level control and lack of garbage collection overhead.
Its focus on zero-cost abstractions contributes to its efficiency.
Leave a Reply