Sling Academy
Home/Rust/Creating Reusable Utility Libraries of Rust Functions

Creating Reusable Utility Libraries of Rust Functions

Last updated: January 03, 2025

Rust is a systems programming language that prioritizes both safety and performance. One of the most beneficial practices in software development is to create reusable utility libraries. This not only promotes clean and well-organized code but also enhances reusability and maintainability for future projects. In this article, we'll explore how to create reusable utility libraries of Rust functions.

What is a Utility Library?

A utility library is a collection of functions that can be used across different applications or modules. These libraries are particularly useful because they encapsulate commonly used logic that has been thoroughly tested and proven to work. This means you don't have to rewrite similar code in multiple places, thereby reducing the chances of introducing bugs.

Setting Up a Rust Project

To get started with creating a utility library, you first need to set up a new Rust project. Use the Rust package manager, Cargo, to create a new library:

$ cargo new my_util_lib --lib

This command creates a new directory named my_util_lib with a template library structure. Navigate into this directory:

$ cd my_util_lib

Defining Reusable Functions

Now, open up the src/lib.rs file. This is where you will define your reusable functions:

// src/lib.rs

/// Adds two numbers together.
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

/// Checks if a number is even.
pub fn is_even(num: i32) -> bool {
    num % 2 == 0
}

/// Computes the factorial of a number.
pub fn factorial(n: u32) -> u32 {
    (1..=n).product()
}

These functions are public (as indicated by pub), making them accessible to other modules or projects that link to this library. The functions demonstrate basic arithmetic, logic, and algorithmic operations, but they can be expanded based on your common needs.

Writing Tests for Your Library

Testing is crucial when creating reusable libraries. Rust includes a built-in test framework to help us ensure our functions behave as expected. Below the function definitions in src/lib.rs, you can add tests:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(1, 2), 3);
    }

    #[test]
    fn test_is_even() {
        assert!(is_even(2));
        assert!(!is_even(3));
    }

    #[test]
    fn test_factorial() {
        assert_eq!(factorial(5), 120);
    }
}

These tests verify that the library's functions work correctly, allowing you to modify and improve the library with confidence.

Publishing the Library

Once your utility library is ready and thoroughly tested, you can publish it on crates.io, Rust's package registry, so that it can be reused in other projects. Start by adding some metadata to your Cargo.toml:


[package]
name = "my_util_lib"
version = "0.1.0"
authors = ["Your Name <[email protected]>"]
description = "A utility library for common number operations"
license = "MIT"

To publish your library, ensure that you have an account on crates.io and have added your API token.

$ cargo login

Then you can publish the package:

$ cargo publish

Your library is now public, and other developers can incorporate it into their own projects using Cargo. They would need to include it in their dependencies:


[dependencies]
my_util_lib = "0.1.0"

Conclusion

Creating a reusable utility library in Rust allows for better project structure and code reuse. With careful planning and thorough testing, you can extend your library over time, catering to more complex and common use cases. Being proficient in designing and distributing reusable libraries is a valuable skill that enhances software development practices, reduces redundancy, and allows you to leverage your work in future projects.

Next Article: Debugging Rust Functions with Print Statements and dbg!()

Previous Article: Storing Functions in Data Structures with Box

Series: Working with Functions in Rust

Rust

You May Also Like

  • E0557 in Rust: Feature Has Been Removed or Is Unavailable in the Stable Channel
  • Network Protocol Handling Concurrency in Rust with async/await
  • Using the anyhow and thiserror Crates for Better Rust Error Tests
  • Rust - Investigating partial moves when pattern matching on vector or HashMap elements
  • Rust - Handling nested or hierarchical HashMaps for complex data relationships
  • Rust - Combining multiple HashMaps by merging keys and values
  • Composing Functionality in Rust Through Multiple Trait Bounds
  • E0437 in Rust: Unexpected `#` in macro invocation or attribute
  • Integrating I/O and Networking in Rust’s Async Concurrency
  • E0178 in Rust: Conflicting implementations of the same trait for a type
  • Utilizing a Reactor Pattern in Rust for Event-Driven Architectures
  • Parallelizing CPU-Intensive Work with Rust’s rayon Crate
  • Managing WebSocket Connections in Rust for Real-Time Apps
  • Downloading Files in Rust via HTTP for CLI Tools
  • Mocking Network Calls in Rust Tests with the surf or reqwest Crates
  • Rust - Designing advanced concurrency abstractions using generic channels or locks
  • Managing code expansion in debug builds with heavy usage of generics in Rust
  • Implementing parse-from-string logic for generic numeric types in Rust
  • Rust.- Refining trait bounds at implementation time for more specialized behavior