Header files

One use of header files in C++ is to expose declarations that are defined in one translation units to other translation units without requiring the duplication of the declarations in multiple files. By convention, declarations that are not included in the header are considered to be private to the defining translation unit (though, to enforce this convention other mechanisms, such as anonymous namespaces, are required).

In contrast, Rust uses neither textually-included header files nor forward declarations. Instead, Rust modules control visibility and linkage simultaneously and expose public definitions for use by other modules.

// person.h
class Person {
  std::string name;

public:
  Person(std::string name) : name(name) {}
  const std::string &getName();
};

// person.cc
#include <string>
#include "person.h"

const std::string &Person::getName() {
  return this->name;
}

// client.cc
#include <string>
#include "person.h"

int main() {
  Person p("Alice");
  const std::string &name = p.getName();

  // ...
}
// person.rs
pub struct Person {
    name: String,
}

impl Person {
    pub fn new(name: String) -> Person {
        Person { name }
    }

    pub fn name(&self) -> &String {
        &self.name
    }
}

// client.rs
mod person;

use person::*;

fn main() {
    let p = Person::new("Alice".to_string());
    // doesn't compile, private field
    // let name = p.name;
    let name = p.name();

    //...
}

In person.rs, the Person type is public but the name field is not. This prevents both direct construction of values of the type (similar to private members preventing aggregate initialization in C++) and prevents field access. The static method Person::new(String) and method Person::name() are exposed to clients of the module by the pub visibility declarations.

In the client module, the mod declaration defines the content of person.rs as a submodule named person. The use declaration brings the contents of the person module into scope.

The essence of the difference

A C++ program is a collection of translation units. Header files are required to make providing forward declarations of definitions from other translation units manageable.

A Rust program is a tree of modules. Definitions in one module may access items from other modules based on visibility declarations given in the definitions of the module themselves.

Submodules and additional visibility features

Modules and visibility declarations are more powerful than shown in the above example. More details on how to use modules, pub, and use to achieve encapsulation goals are described in the chapter on private members and friends.