Modules and Visibility

Module Organization in Libraries

Well-structured Rust libraries keep internal implementation modules private (mod ...;) and curate a flat, deliberate public API in src/lib.rs using pub use re-exports, logical feature submodules, and an optional prelude module.

Core Principles of Rust Library Architecture

Principle Implementation Technique Benefit
Private implementation modules Declare mod parser; mod buffer; (without pub) in src/lib.rs Freedom to rename, split, or reorganize internal files without breaking downstream users
Curated root re-exports pub use crate::client::Client; in src/lib.rs Users import primary types directly from the crate root (use my_lib::Client;)
Unified crate Error and Result<T> pub use crate::error::{Error, Result}; Every public function in the library returns a consistent my_lib::Result<T>
bin + lib split for CLI apps Put core logic in src/lib.rs and keep src/main.rs as a thin CLI wrapper Integration tests in tests/ and benchmarks in benches/ can import src/lib.rs directly
Recommended Library Crate Structure (`src/`):

  src/
  ├── lib.rs          <-- Public facade: `mod client; mod error; pub use client::Client; ...`
  ├── main.rs         <-- (Optional) Thin CLI binary calling `my_crate::run()`
  ├── error.rs        <-- `pub enum Error` + `pub type Result<T> = std::result::Result<T, Error>;`
  ├── client.rs       <-- Core implementation (`pub struct Client`)
  └── codec/          <-- Internal subsystem (`mod codec;` is private to external users)
      ├── frame.rs    <-- Uses `pub(crate)` to share types with `client.rs`
      └── varint.rs

Example: Clean src/lib.rs Facade Over Private Submodules

// --- Internal private modules (not exposed in public docs as module paths) ---
mod error {
    use std::fmt;

    #[derive(Debug, PartialEq)]
    pub enum Error {
        EmptyKey,
        QuotaExceeded { limit: usize },
    }

    impl fmt::Display for Error {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                Self::EmptyKey => write!(f, "key must not be empty"),
                Self::QuotaExceeded { limit } => write!(f, "quota of {limit} items exceeded"),
            }
        }
    }

    pub type Result<T> = std::result::Result<T, Error>;
}

mod store {
    use crate::error::{Error, Result};
    use std::collections::HashMap;

    pub struct KvStore {
        entries: HashMap<String, String>,
        max_entries: usize,
    }

    impl KvStore {
        pub fn with_Quota(max_entries: usize) -> Self {
            Self {
                entries: HashMap::new(),
                max_entries,
            }
        }

        pub fn insert(&mut self, key: &str, value: &str) -> Result<()> {
            if key.is_empty() {
                return Err(Error::EmptyKey);
            }
            if !self.entries.contains_key(key) && self.entries.len() >= self.max_entries {
                return Err(Error::QuotaExceeded {
                    limit: self.max_entries,
                });
            }
            self.entries.insert(String::from(key), String::from(value));
            Ok(())
        }

        pub fn get(&self, key: &str) -> Option<&str> {
            self.entries.get(key).map(String::as_str)
        }
    }
}

// --- Public API surface re-exported at the crate root ---
pub use self::error::{Error, Result};
pub use self::store::KvStore;

fn main() {
    let mut db = KvStore::with_Quota(1);
    db.insert("region", "us-east").unwrap();

    println!("region = {:?}", db.get("region"));
    println!("2nd insert error = {:?}", db.insert("env", "prod"));
}
region = Some("us-east")
2nd insert error = Err(QuotaExceeded { limit: 1 })