Rust
#13
TIOBE#18
PYPL#8
GitHub#25
RedMonk#19
IEEESpectrum#11
JetBrains#14
Programming Language
Rust
Overview
Rust is a systems programming language that combines memory safety with high performance.
Details
Rust was initially developed by Graydon Hoare at Mozilla starting in 2010 as a systems programming language. Its main feature is the ownership system that guarantees memory safety without using garbage collection. It maintains high performance like C/C++ while preventing issues such as memory leaks, buffer overflows, and data races at compile time. It's being adopted in areas where performance is critical, such as web browser engines, operating systems, WebAssembly, cryptocurrencies, and game engines. Recently, it's also beginning to be adopted in Linux kernel development.
Code Examples
Hello World
// Basic output
fn main() {
println!("Hello, World!");
// Output using variables
let message = "Hello, Rust!";
println!("{}", message);
// Output using format strings
let name = "John";
let age = 25;
println!("My name is {} and I am {} years old.", name, age);
}
Variables and Ownership
fn main() {
// Immutable variables (default)
let name = "John";
let age = 25;
// Mutable variables
let mut score = 85;
score = 90; // Can be changed
// Type annotations
let height: f64 = 175.5;
let is_active: bool = true;
// Ownership move
let s1 = String::from("Hello");
let s2 = s1; // Ownership of s1 moves to s2
// println!("{}", s1); // Error: s1 cannot be used
println!("{}", s2);
// Clone (copy)
let s3 = String::from("World");
let s4 = s3.clone(); // Clone s3
println!("{} {}", s3, s4); // Both can be used
// Borrowing (references)
let s5 = String::from("Rust");
let len = calculate_length(&s5); // Borrow
println!("The length of '{}' is {}.", s5, len); // s5 can still be used
}
fn calculate_length(s: &String) -> usize {
s.len() // Borrowing doesn't move ownership
}
Data Types and Collections
fn main() {
// Basic types
let integer: i32 = 42;
let float: f64 = 3.14;
let character: char = '🦀';
let boolean: bool = true;
// Tuples
let person: (String, i32, f64) = (String::from("Tanaka"), 30, 175.5);
let (name, age, height) = person; // Destructure
println!("Name: {}, Age: {}, Height: {}cm", name, age, height);
// Arrays (fixed length)
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
let first = numbers[0];
// Vectors (variable length)
let mut fruits = vec!["apple", "banana", "orange"];
fruits.push("grape"); // Add element
// HashMap
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Math"), 85);
scores.insert(String::from("English"), 92);
scores.insert(String::from("Science"), 78);
// Pattern matching for access
match scores.get("Math") {
Some(score) => println!("Math score: {}", score),
None => println!("Math score not found"),
}
println!("Fruits: {:?}", fruits);
println!("Scores: {:?}", scores);
}
Structs and Methods
// Struct definition
#[derive(Debug)] // Enable debug output
struct Person {
name: String,
age: u32,
email: String,
}
// Method implementation
impl Person {
// Associated function (constructor-like)
fn new(name: String, age: u32, email: String) -> Person {
Person { name, age, email }
}
// Method (using &self)
fn get_info(&self) -> String {
format!("{} ({} years old) - {}", self.name, self.age, self.email)
}
// Mutable method (using &mut self)
fn set_age(&mut self, new_age: u32) {
self.age = new_age;
}
// Consuming method (using self)
fn into_email(self) -> String {
self.email
}
}
// Tuple struct
struct Point(f64, f64);
impl Point {
fn distance_from_origin(&self) -> f64 {
(self.0 * self.0 + self.1 * self.1).sqrt()
}
}
fn main() {
// Create struct instance
let mut person = Person::new(
String::from("John Doe"),
25,
String::from("[email protected]"),
);
println!("{}", person.get_info());
// Call mutable method
person.set_age(26);
println!("Updated: {}", person.get_info());
// Tuple struct
let point = Point(3.0, 4.0);
println!("Distance from origin: {:.2}", point.distance_from_origin());
// Debug output
println!("Debug: {:?}", person);
}
Enums and Pattern Matching
// Enum definition
#[derive(Debug)]
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
// Option type usage example
fn find_word(text: &str, word: &str) -> Option<usize> {
text.find(word)
}
// Result type usage example
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("Cannot divide by zero"))
} else {
Ok(a / b)
}
}
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("Quit the program"),
Message::Move { x, y } => println!("Move to ({}, {})", x, y),
Message::Write(text) => println!("Message: {}", text),
Message::ChangeColor(r, g, b) => {
println!("Change color to RGB({}, {}, {})", r, g, b)
}
}
}
}
fn main() {
// Using enums
let messages = vec![
Message::Move { x: 10, y: 20 },
Message::Write(String::from("Hello, Rust!")),
Message::ChangeColor(255, 0, 0),
Message::Quit,
];
for message in messages {
message.call();
}
// Using Option type
let text = "Hello, Rust programming!";
match find_word(text, "Rust") {
Some(index) => println!("'Rust' found at position {}", index),
None => println!("'Rust' not found"),
}
// Concise pattern matching with if let
if let Some(index) = find_word(text, "programming") {
println!("'programming' found at position {}", index);
}
// Using Result type
match divide(10.0, 3.0) {
Ok(result) => println!("10.0 ÷ 3.0 = {:.2}", result),
Err(error) => println!("Error: {}", error),
}
// Concise error handling with ? operator
if let Err(e) = try_division() {
println!("Calculation error: {}", e);
}
}
fn try_division() -> Result<(), String> {
let result = divide(5.0, 0.0)?; // Early return on error
println!("Result: {}", result);
Ok(())
}
Traits (Interfaces)
// Trait definition
trait Summary {
fn summarize(&self) -> String;
// Default implementation
fn announce(&self) {
println!("Summary: {}", self.summarize());
}
}
// Struct definitions
struct NewsArticle {
headline: String,
location: String,
author: String,
content: String,
}
struct Tweet {
username: String,
content: String,
reply: bool,
retweet: bool,
}
// Trait implementation
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{} by {} ({})", self.headline, self.author, self.location)
}
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
// Function using trait bounds
fn notify(item: &impl Summary) {
println!("Breaking news: {}", item.summarize());
}
// Multiple trait bounds
use std::fmt::Display;
fn some_function<T>(item: &T) -> String
where
T: Summary + Display,
{
format!("Display: {} | Summary: {}", item, item.summarize())
}
// Implementing standard library traits
impl Display for Tweet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Tweet by {}: {}", self.username, self.content)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Rust programming language gaining popularity"),
location: String::from("Tokyo"),
author: String::from("John Doe"),
content: String::from("Rust adoption is increasing..."),
};
let tweet = Tweet {
username: String::from("rust_lover"),
content: String::from("Programming in Rust!"),
reply: false,
retweet: false,
};
// Call trait methods
article.announce();
tweet.announce();
// Call function with trait bounds
notify(&article);
notify(&tweet);
// Multiple trait bounds
println!("{}", some_function(&tweet));
}
Lifetimes
// Lifetime annotations
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
// Lifetimes in structs
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
// Lifetime elision rules automatically infer 'a
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention please: {}", announcement);
self.part
}
}
fn main() {
let string1 = String::from("long string is long");
{
let string2 = String::from("xyz");
let result = longest(string1.as_str(), string2.as_str());
println!("The longest string is {}", result);
}
// Using lifetimes in structs
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let excerpt = ImportantExcerpt {
part: first_sentence,
};
println!("Important excerpt: {}", excerpt.part);
let announcement = "A new chapter begins";
println!("Return value: {}", excerpt.announce_and_return_part(announcement));
}
Concurrent Programming
use std::thread;
use std::time::Duration;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
fn main() {
// Basic threading
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap(); // Wait for thread completion
// Channel communication
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let vals = vec![
String::from("hi"),
String::from("from"),
String::from("the"),
String::from("thread"),
];
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
for received in rx {
println!("Got: {}", received);
}
// Shared state (Mutex)
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::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());
}
Error Handling and Testing
// Custom error type
use std::fmt;
use std::error::Error;
#[derive(Debug)]
struct CustomError {
message: String,
}
impl fmt::Display for CustomError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Custom error: {}", self.message)
}
}
impl Error for CustomError {}
// Function that returns error
fn divide_numbers(a: f64, b: f64) -> Result<f64, Box<dyn Error>> {
if b == 0.0 {
Err(Box::new(CustomError {
message: String::from("Cannot divide by zero"),
}))
} else {
Ok(a / b)
}
}
// Test function
pub fn add_two(a: i32) -> i32 {
a + 2
}
fn main() {
// Error handling examples
match divide_numbers(10.0, 2.0) {
Ok(result) => println!("10 ÷ 2 = {}", result),
Err(e) => println!("Error: {}", e),
}
match divide_numbers(10.0, 0.0) {
Ok(result) => println!("10 ÷ 0 = {}", result),
Err(e) => println!("Error: {}", e),
}
// Panic example (program crashes)
// panic!("This is a panic!");
}
// Test module
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_adds_two() {
assert_eq!(4, add_two(2));
}
#[test]
fn test_divide_numbers() {
assert_eq!(divide_numbers(10.0, 2.0).unwrap(), 5.0);
assert!(divide_numbers(10.0, 0.0).is_err());
}
#[test]
#[should_panic]
fn test_panic() {
panic!("This test should panic");
}
}
Versions
Version | Release Date | Major Features |
---|---|---|
Rust 1.81 | 2024-09 | Error trait in core, Lint against ambiguous wide pointer comparisons |
Rust 1.80 | 2024-07 | LazyCell and LazyLock, Exclusive ranges in patterns |
Rust 1.79 | 2024-06 | Inline const expressions, Bounds in associated type position |
Rust 1.78 | 2024-05 | Diagnostic attributes, assert_unsafe_precondition |
Rust 1.77 | 2024-03 | C-string literals, Offset of struct fields |
Rust 1.76 | 2024-02 | Arc::unwrap_or_clone, Inspection functions on Option and Result |
Rust 1.75 | 2023-12 | async fn and return-position impl trait in traits |
Reference Links
Official Documentation
- Rust Official Site - Official website
- The Rust Programming Language - The Rust Book
- Rust by Example - Learn Rust with examples
Learning Resources
- Rustlings - Rust learning exercises
- Rust Reference - Rust language reference
- The Cargo Book - Cargo package manager guide
Development Tools
- crates.io - Rust package registry
- Rust Playground - Try Rust in your browser
- Awesome Rust - Collection of Rust libraries and tools