RustBrock/Publishing libraries.md
darkicewolf50 f3ec0d98cc
Some checks failed
Test Gitea Actions / first (push) Successful in 12s
Test Gitea Actions / check-code (push) Failing after 21s
Test Gitea Actions / test (push) Has been skipped
Test Gitea Actions / documentation-check (push) Has been skipped
finsihed ch14.1 and more than halfway through ch14.2
2025-02-25 17:02:50 -07:00

11 KiB

Publishing a Crate to Crates.io

We have used packages form crates.io as dependencies of our pojects.

You could also share your code with others by publishing your own packages.

The crate regsitry at crates.io distributes the source code of your packages, it primarily hosts code that is open source.

Rust and Cargo have features that make your published package easier for people to find and use.

These features will be covered later and then explaining how to publish a package

Making Useful Documentation Comments

Accurately documenting your packages will others know how and when to use them.

It is worth investing the time to write documentation

In ch we said that to comment in Rust you use two slashes, //

Rust also has a pariticualr kind of comment for documentation.

This is known conveniently as a documentation comments, that will generate HTML documentation.

The HTML diesplays the contents of documentation comments for public API items intended for programmers interested in knowing how to use your crate as opposed to how your crate is implemented.

The documentation comments uses three slashes /// instead of two and this supports Markdown notation for formatting the text.

The placement of documentation comments just before the item they are documenting.

Here is an example of documentation comments for an add_one function in a crate named my_crate.

/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

Here we first give a description of what the add_one function does.

Then we add a section starting with the heading Examples, and then provide code that demonstrates how to use the add_one function.

We can generate te HTML documentation from this documentation comment by running cargo doc.

This command runs the rsutdoc tool which comes distributed with Rust and pts the generated HTML documentation in the target/doc directory.

For convenience running cargo doc --open will build the HTML for your current crate's documentation as well as the documentation for all of your crate's dependencies then it will open the result in a web browser.

Navigating to the add_one function you will see how the texxt in the documentation comments is rendered as shown below

Commonly Used Sections

We previously used the # Examples Markdown heading to create a section in the HTML with the titl "Examples".

Here are some other secions that crate author commonly use in the documentation

  • Panics: The scenarios in which the function being documented could panic. Funtion callers who don't want thier programs to panic should make sure they dont call the function in this way/situation
  • Errors: If the function returns a Result, that describes the kinds of errors that may occur and what conditions might cause those errors to be returned. This can be helpful to caller so that they can write code to handle different kinds of errors in different ways.
  • Safety: If the function is inheritly unsafe to call, there should be a section explaing why the function is unsafe and covering the invariants that the function expects callers to uphold.

Most documentation comments don't need all of these sctions but this is a good checklist ot remind you of the aspects of your code users will be interested in knowing.

Documentation Comments as Tests

Adding example code blocks in your documentation comments can help demonstrate how to use the library.

Doing so also has an addtional bonus: running cargo test will run the code examples in your documentation as tests

This ensures that your examples will be tested to work, because that would be the worst to ship an example that doesn't work.

If we run cargo test with the documentation for add_one function we would get this output

   Doc-tests my_crate

running 1 test
test src/lib.rs - add_one (line 5) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.27s

Now if we change either the function or the example so the assert_eq! in the example panics, then we will see that the doc tests catch that the example and the code are out of sync with each other.

Commenting Contained Items

The style of commnet //! adds documentation to the item that contains the comments rather than to the items following the comments.

We typically use these doc comments inside the crate root file (src/lib.rs by convention) or inside a module to document the crate or the module as a whole.

For example, adding documentation that describes the purpose of the my_crate that contains the add_one function

We add documentation comments qith //! to the beginning of the src/lib.rs file

Here is an example of this

//! # My Crate
//!
//! `my_crate` is a collection of utilities to make performing certain
//! calculations more convenient.

/// Adds one to the number given.
// --snip--
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

Notice that there isn't any code after the last line that begins with //!

Because we started the comments with //! instead of /// , this inidcates we are documenting the item that contains this comment rather than an item that follows his comment.

In this case, that item is the src/lib.rs file which is the crate root.

These comments describe the entire crate

When we run cargo doc --open, these comments will display on the front page of the documentation for my_crate above the list of public items in the crate

Here is what the docs would look like

Documentation comments within items are useful for describing crates and modules especially.

Use them to explain the overall purpose of the container to help your users understand the crate's organization

Exporting a Convenient Public API with pub use

The structure of your public API is a major consideration when publishing a crate.

People who use your crate are less familiar with the structure than you.

They might have difficulty finding the pieces they want to use if your crate has a large moudle hierarchy

Before we covered how to make items public using the pub keyword and bring items into a scope iwth the use keyword.

The structure that makes sense to you while you are developing the crate might not be very useful or convenient for your users.

You might want to organize your structs in a heirary containing multiple levels.

The people who want to use a type you have defined deep in the hierary might have trouble finding out that tpye exists.

They might also be annoyed having to enter use my_crate::some_module::another_module::UsefulType; rather than use my_crate::UsefulType;.

The good news is that if the struct isn't convenient for others to use from another library, you dont have to rearrange your interal organization.

You can instead re-export items to make a public structure that is different form your private structure by using pub use.

Re-exporting takes a public item in one location and makes it public in another location as if it we defined in the other location instead

For example lets say we made a libarary named art

Within this library there are two modules:

  • a kinds module containing two enums named PrimaryColor and SecondaryColor
  • a utils module containinga function named mix

Here is what it would look like in src/lib.rs

//! # Art
//!
//! A library for modeling artistic concepts.

pub mod kinds {
    /// The primary colors according to the RYB color model.
    pub enum PrimaryColor {
        Red,
        Yellow,
        Blue,
    }

    /// The secondary colors according to the RYB color model.
    pub enum SecondaryColor {
        Orange,
        Green,
        Purple,
    }
}

pub mod utils {
    use crate::kinds::*;

    /// Combines two primary colors in equal amounts to create
    /// a secondary color.
    pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
        // --snip--
    }
}

Here is what the frontpage docs generated by cargo doc would look like

Notice that the PirmaryColor and SecondaryColor types are't listed on the front page nor is the mix function.

We have to navigate to kinds and utils to see them.

Another crate that depends on this library would need use statements that bring items from art into scope. Specifying the module structure that is currently defined

Here is an example of a crate that uses the PrimaryColor and mix items form the art crate

use art::kinds::PrimaryColor;
use art::utils::mix;

fn main() {
    let red = PrimaryColor::Red;
    let yellow = PrimaryColor::Yellow;
    mix(red, yellow);
}

The author of the code in above which uses the art crate had to figure out that PrimaryColor is in the kinds module and mix is in the utils module.

The module struct of the art crate is more relevant to developers working on the art rate than to those using it.

The internal structure doesn't contain any useful info for someone trying to understand how to use the art crate, but rather causes confusion becasue devs who use it have to figure out where to look and must specify the module names in the use statements.

To remove the interal organization from the public API, we can modify the art crate to add pub use statements to re-export the items at the top level

//! # Art
//!
//! A library for modeling artistic concepts.

pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;

pub mod kinds {
    // --snip--
}

pub mod utils {
    // --snip--
}

The API docs that cargo doc generates for this crate will now list and link re-exprots on the front page.

This makes the PrimaryColor and SecondaryColor types and makes finding the mix function easier

The art crate users can still see the internal structure as demonstrated in the picture above.

They could also use the more convenient structure like in the example below

use art::mix;
use art::PrimaryColor;

fn main() {
    // --snip--
}

In cases where there are many nested modules, re-exporting the types at the top level with pub use.

This can make a very significant difference in the experience of people who use the crate.

Another common use of pub use is to re-export definitions of a dependency in the current crate to make that crate's definitions part of your crate's public API.

Creating a useful public API structure is more of an art than a science.

You can iterate to find the API that works best for your users.

Choosing pub use gives you flexibility in how you structure your crate internally and decouples that interanl struct from what you present to your users.

Look at some of the code of crates you have used previously to see if their internal struct differs from their public API.

Setting Up a Crates.io Account

Before being able to create