Std Miscellaneous Features
Threads
Rust provides a mechanism for spawning native threads via the spawn
function from the std::thread
module, the argument of this function is a moving closure.
use std::thread;
fn main() {
// Spawn a new thread and print numbers from 1 to 5
let handle1 = thread::spawn(|| {
for i in 1..=5 {
println!("Thread 1: Count {}", i);
thread::sleep(std::time::Duration::from_millis(200));
}
});
// Spawn another thread and print letters from 'A' to 'E'
let handle2 = thread::spawn(|| {
for ch in 'A'..'F' {
println!("Thread 2: Letter {}", ch);
thread::sleep(std::time::Duration::from_millis(300));
}
});
// Wait for both threads to finish
handle1.join().unwrap();
handle2.join().unwrap();
println!("Main thread exiting.");
}
Channels
In Rust, channels are a way for threads to communicate by sending and receiving messages.
use std::thread;
use std::sync::mpsc; // mpsc stands for multiple producer, single consumer
fn main() {
// Create a channel with a sender and a receiver
let (sender, receiver) = mpsc::channel();
// Spawn a new thread to send messages
let sender_thread = thread::spawn(move || {
for i in 1..=5 {
// Send a message to the receiver
sender.send(i).unwrap();
thread::sleep(std::time::Duration::from_millis(200));
}
});
// Main thread receiving messages
for _ in 1..=5 {
// Receive a message from the sender
let received_message = receiver.recv().unwrap();
println!("Received: {}", received_message);
}
// Wait for the sender thread to finish
sender_thread.join().unwrap();
println!("Main thread exiting.");
}
Path
The std::path::Path
type is used to represent file system paths. It provides methods for working with file and directory paths in a platform-independent way.
use std::path::Path;
fn main() {
// Create a Path from a string
let path_str = "/home/user/documents/example.txt";
let path = Path::new(path_str);
// Display the path components
println!("File Name: {:?}", path.file_name());
println!("Parent Directory: {:?}", path.parent());
println!("Extension: {:?}", path.extension());
// Combine paths
let new_path = path.join("subdirectory").join("newfile.txt");
println!("Combined Path: {:?}", new_path);
// Check if the path exists
println!("Path exists: {}", path.exists());
// Iterate over path components
println!("Path Components:");
for component in path.components() {
println!("{:?}", component);
}
}
File I/O
The File
struct represents a file that has been opened, and gives read and write access to the underlying file.
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
fn main() -> io::Result<()> {
// File path
let file_path = "example.txt";
// Open or create a file for writing
let mut file = File::create(file_path)?;
// Write some content to the file
writeln!(file, "Hello, Literank!")?;
writeln!(file, "This is a second line.")?;
// Open the file for reading
let file = File::open(file_path)?;
// Read and print each line from the file
let reader = BufReader::new(file);
for line in reader.lines() {
println!("{}", line?);
}
Ok(())
}
Child Processes
The std::process::Command
type is used to spawn child processes.
use std::process::Command;
use std::io;
fn main() -> io::Result<()> {
// Command to execute: echo "Hello, LiteRank!"
let command = Command::new("echo")
.arg("Hello, LiteRank!")
.output()?;
// Check if the command was successful
if command.status.success() {
// Print the captured output as a string
let stdout_str = String::from_utf8_lossy(&command.stdout);
println!("Output: {}", stdout_str);
} else {
// Print the error message
let stderr_str = String::from_utf8_lossy(&command.stderr);
eprintln!("Error: {}", stderr_str);
}
Ok(())
}
fs Operations
The std::fs
module in Rust provides various functions for common file system operations.
use std::fs::{self, File};
use std::io::{self, Read, Write};
fn main() -> io::Result<()> {
// Create a directory
fs::create_dir("literank_dir")?;
// Check if a path exists
let path_exists = fs::metadata("literank_dir").is_ok();
println!("Does literank_dir exist? {}", path_exists);
// Write content to a file
let file_path = "literank_dir/example_file.txt";
let mut file = File::create(file_path)?;
write!(file, "Hello, Literank!")?;
// Read content from a file
let mut buffer = String::new();
let mut file = File::open(file_path)?;
file.read_to_string(&mut buffer)?;
println!("Content of {}: {}", file_path, buffer);
// Rename a file
fs::rename(file_path, "literank_dir/renamed_file.txt")?;
// List the contents of a directory
println!("Contents of literank_dir:");
for entry in fs::read_dir("literank_dir")? {
if let Ok(entry) = entry {
println!("{}", entry.file_name().to_string_lossy());
}
}
// Remove a file
fs::remove_file("literank_dir/renamed_file.txt")?;
// Remove a directory
fs::remove_dir("literank_dir")?;
Ok(())
}
Program Arguments
The std::env::args
function in Rust returns an iterator over the command-line arguments passed to the program, including the executable name as the first argument.
use std::env;
fn main() {
// Iterate over command-line arguments
for (index, arg) in env::args().enumerate() {
println!("Arg {} : {}", index, arg);
}
}
Foreign Function Interface
Foreign Function Interface (FFI) in Rust allows you to call functions written in other languages and be called by other languages.
The C file (example.c) looks like this:
#include <stdio.h>
// A simple C function
void greet_from_c(const char *name) {
printf("Hello, %s, from C!\n", name);
}
Compile the C code:
$ gcc -c example.c -o example.o
$ gcc example.o -o libexample.so -shared
The Rust file (main.rs) looks like this:
// Define the extern block that links to the `libexample` library
#[link(name = "example")]
extern "C" {
fn greet_from_c(name: *const std::os::raw::c_char);
}
fn main() {
// Call the C function from Rust
let name = "Rust";
unsafe {
greet_from_c(name.as_ptr() as *const i8);
}
}
Compile the Rust code:
$ rustc main.rs
The Rust code uses extern "C"
to define an interface to the C function greet_from_c
. The unsafe
block is necessary because calling foreign functions is considered unsafe in Rust.
Code Challenge
Try to modify the code in the editor to make it print:
Squared result: 1 Squared result: 4 Squared result: 9 Squared result: 16 Squared result: 25 Squared result: 36 Squared result: 49 Squared result: 64