» Quick Introduction to Rust » 3. Std Library » 3.2 Std Misc.

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
Loading...
> code result goes here
Prev
Next