Library
When you use cargo to create a library, the primary difference is the creation of a lib.rs file, instead of main.rs and a “Hello, World” application. The lib.rs file contains sample code within a function that performs a trivial mathematical operation. All of this occurs within the context of a unit test. The following command creates a new package with a library crate:
cargo new --lib packagename
Unlike executable binaries, libraries are not self-executing. Placing the source code for the library within a unit test provides a mechanism for executing that code. You can execute the library crate with the following command:
cargo test
This command will run unit tests in the crate. For lib.rs, this provides the opportunity to execute and test the source code in the library. As a result, the cargo test command displays whether the unit test passed or failed.
Listing 2.4 shows the lib.rs that cargo creates, with sample code.
Code Listing 2.4. Cargo-generated lib.rs file.pub
fn add(left: usize, right: usize) -> usize { left + right } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let result = add(2, 2); assert_eq!(result, 4); } }
Let’s explore the lib.rs crate. The file starts with the #[cfg(test)] annotation. This annotation asks the cargo build command to ignore unit tests and not to include them in the resulting binary. Within the file, each unit test is labeled with the #[test] annotation. The sample unit test performs a simple addition. The result of the operation is compared to the expected value within the assert_eq! macro. You update the code in the unit tests to reference the specific public functions of your library. There should be a unit test for each public function in the library.
Listing 2.5 provides an example of a library crate with two functions. The get_hello function is public and returns the “Hello, world!” string. The test_get_hello function is a unit test and tests the get_hello function.
Code Listing 2.5. Example unit test and target function.pub
fn get_hello()->String { "Hello, world!".to_string() } #[cfg(test)] mod tests { #[test] fn test_get_hello() { let result = get_hello(); assert_eq!(result, "Hello, world!"); } }
You may want to create an executable crate that uses the library. The cargo.toml file for the executable must be updated to reference the library. Listing 2.6 shows the updated cargo.toml that references a local version of the hello library in the dependency section. The change in the file is highlighted in bold.
Code Listing 2.6. Updated cargo.toml with hello dependency
[package] name = "use_hello" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/ manifest.html [dependencies] hello={path = "../hello" }
With the preceding cargo.toml file, the executable crate can access the public functions of the library. The syntax for accessing functions in the library is libraryname::function. The code in Listing 2.7 calls the get_hello function found in the hello library.
Code Listing 2.7. Calling get_hello in the hello library
fn main() { println!(“{}”, hello::get_hello()); }
You can now use the cargo run command to run the executable crate and display the “Hello, world!” string.