инкапсуляция выше уровня классаencapsulation above the class level
mod определяет именованное, вложенное пространство имён внутри крейта. По умолчанию всё в Rust приватно для своего модуля и его потомков — pub (и его более гранулярные формы) явно открывает отдельные элементы наружу, а компилятор реально это проверяет, а не просто советует.mod defines a named, nestable namespace within a crate. By default, everything in Rust is private to its module and that module's descendants — pub (and its more granular forms) explicitly opts individual items into visibility, and the compiler actually enforces this rather than merely advising it.
mod определяет модуль — именованное, вкладываемое пространство имён для организации элементов (функций, структур, enum'ов, трейтов, других модулей) внутри крейта (крейт — единица компиляции верхнего уровня, примерно аналогичная сочетанию единицы трансляции и слинкованной библиотеки в C++, хотя аналогия не точна). По умолчанию ВСЁ в Rust ПРИВАТНО для модуля, в котором определено, и его потомков — ключевое слово pub (и его более гранулярные формы: pub(crate), видимое где угодно в текущем крейте, но не снаружи; pub(super), видимое только родительскому модулю; pub(in path::to::module), видимое конкретному именованному пути модуля) явно включает отдельные элементы в видимость снаружи их определяющего модуля.mod defines a module — a named, nestable namespace for organizing items (functions, structs, enums, traits, other modules) within a crate (a crate being the top-level compilation unit, roughly analogous to a C++ translation-unit-plus-linked-library combination, though the analogy isn't exact). By default, EVERYTHING in Rust is PRIVATE to the module it's defined in and that module's descendants — the pub keyword (and its more granular forms, pub(crate) visible anywhere in the current crate but not outside it, pub(super) visible to the parent module only, pub(in path::to::module) visible to a specific named module path) explicitly opts individual items into being visible to callers outside their defining module.
Заметка про edition: сама система приватности (описанная выше) не менялась, но синтаксис путей в use/объявлениях модулей заметно упростился в Rust 2018. До 2018 каждый файл с внешними крейтами начинался с extern crate serde;, а относительные и абсолютные пути в use было легко перепутать. Edition 2018 убрал необходимость в extern crate для обычных зависимостей и сделал пути в use единообразными независимо от глубины вложенности модуля — это разовое изменение синтаксиса, а не изменение самой модели видимости из этого файла.Edition note: the privacy system itself (described above) didn't change, but the path syntax in use/module declarations was noticeably simplified in Rust 2018. Before 2018, every file using external crates started with extern crate serde;, and relative vs. absolute paths in use were easy to confuse. Edition 2018 removed the need for extern crate for ordinary dependencies and made use paths uniform regardless of the module's nesting depth — a one-time syntax change, not a change to the visibility model covered in this file.
Позиция "приватно по умолчанию" — прямая инверсия дефолта C++ (члены struct публичны по умолчанию, члены class приватны, но эквивалента приватности на уровне модуля нет вовсе — свободная функция или публичный член класса, объявленные где угодно в единице трансляции, видны любой другой единице трансляции, включившей её заголовок, лишь по соглашению/документации о том, что "внутреннее").This "private by default" stance is the direct inverse of C++'s class-based default (C++ struct members are public by default, class members private by default, but there's no equivalent module-level privacy at all — a free function or a public class member declared anywhere in a translation unit is visible to any other translation unit that includes its header, subject only to convention/documentation about what's "internal").
Приватность модулей Rust — настоящая, проверяемая компилятором граница инкапсуляции на уровне ВЫШЕ отдельных классов: внутренние вспомогательные функции/типы целого модуля можно свободно рефакторить, не ломая ни одного внешнего вызывающего кода, потому что компилятор сам гарантирует, что снаружи модуля их использовать не могли.Rust's module privacy is a genuine, compiler-enforced encapsulation boundary at a granularity ABOVE individual classes: a whole module's internal helper functions/types can be freely restructured without breaking any external caller, because the compiler itself guarantees nothing outside the module could have been using them.
Это закрывает реальный разрыв, где "внутренние" API на основе заголовков в C++ обеспечиваются соглашением/именованием (detail::, суффиксы _impl), а не компилятором.This closes a real gap where C++ header-based "internal" APIs are enforced by convention/naming (detail::, _impl suffixes) rather than by the compiler.
Модуль можно объявить прямо внутри файла; закрытая вспомогательная функция вызывается только изнутри модуля и его потомков, а pub fn делает конкретную функцию вызываемой снаружи:A module can be declared right inside a file; a private helper function is callable only from within that module and its children, while pub fn makes a specific function callable from outside:
mod parser {
fn tokenize(input: &str) -> Vec<String> { // приватно — видно только внутри parser// private — visible only inside parser
input.split_whitespace().map(String::from).collect()
}
pub fn parse(input: &str) -> Vec<String> { // единственная публичная точка входа// the single public entry point
tokenize(input)
}
}
Модули можно также разносить по отдельным файлам — mod foo; ищет foo.rs или foo/mod.rs; точное соглашение о раскладке файлов ортогонально самой концепции приватности и здесь не разбирается подробно. pub(crate) — очень часто используемая золотая середина: "видно где угодно в этом крейте, но не выставлено наружу как публичный API библиотеки":Modules can also be split across separate files — mod foo; looks for foo.rs or foo/mod.rs; the exact file-layout convention is somewhat orthogonal to the core privacy concept and isn't detailed here. pub(crate) is a very commonly used middle ground: "visible anywhere in this crate, but not exposed externally as the library's public API":
pub(crate) struct InternalError { code: i32 } // видно всему крейту, но не внешним пользователям библиотеки// visible crate-wide, but not to external users of the library
Модуль parser с приватным внутренним fn tokenize(...) и единственной pub fn parse(...) — паттерн "спрячь детали реализации, покажи одну чистую точку входа", проверяемый компилятором, а не соглашением.A parser module with a private internal fn tokenize(...) helper and a single pub fn parse(...) entry point — the "hide implementation details, expose one clean entry point" pattern directly enforced by the compiler rather than by convention.
mod parser {
fn tokenize(s: &str) -> Vec<String> { /* ... */ Vec::new() }
pub fn parse(s: &str) -> Vec<String> { tokenize(s) }
}
// parser::tokenize(...) снаружи модуля — ОШИБКА КОМПИЛЯЦИИ: приватная функция// parser::tokenize(...) from outside the module — COMPILE ERROR: private function
Общий внутренний тип ошибки, используемый несколькими внутренними подсистемами, но никогда не появляющийся в публичном API крейта, если он публикуется как библиотека — в отличие от pub, который выставил бы его наружу тоже.A shared internal error type used by multiple internal subsystems, but that should never appear in the crate's public API if it's published as a library — contrasted with pub, which would expose it externally too.
pub(crate) enum InternalError {
ParseFailed,
IoFailed,
}
// видно всем модулям этого крейта, но не пользователям крейта-библиотеки// visible to every module in this crate, but not to users of the crate as a library
Структура с частью публичных и частью приватных полей — видимость контролируется по КАЖДОМУ полю отдельно, позволяя выставлять одни данные напрямую, оставляя другие манипулируемыми только через собственные методы структуры.A struct with some pub fields and some private fields — visibility is controlled per-field, letting a struct expose certain data directly while keeping other fields as implementation details manipulable only through its own methods.
pub struct Connection {
pub id: u64, // доступно напрямую снаружи// directly accessible from outside
internal_state: u8, // приватно — только через методы Connection// private — only through Connection's own methods
}
use std::collections::HashMap; сокращает путь вместо постоянного написания полного; pub use inner_module::Thing; позволяет крейту показать плоскую, курируемую поверхность API, отличную от реальной вложенной структуры модулей внутри.use std::collections::HashMap; shortens the path instead of always writing the fully-qualified one; pub use inner_module::Thing; lets a crate present a flattened, curated API surface distinct from its actual internal nested module tree.
use std::collections::HashMap; // вместо std::collections::HashMap::new() каждый раз// instead of std::collections::HashMap::new() every time
mod inner_module {
pub struct Thing;
}
pub use inner_module::Thing; // крейт::Thing напрямую, без крейт::inner_module::Thing// crate::Thing directly, without crate::inner_module::Thing
Для структуры pub-структура не делает поля (обычные или кортежные) публичными автоматически — им нужен собственный pub, чтобы быть доступными снаружи; частая ранняя ошибка — сделать структуру pub и не понимать, почему внешний код всё ещё не может её сконструировать напрямую или получить доступ к полям. При этом у enum'ов асимметрия реальная: варианты enum'а автоматически настолько же видимы, насколько виден сам enum, если он pub — это стоит проверять по актуальной документации в конкретном случае, а не считать структуры и enum'ы одинаковыми в этом отношении.For a struct, being pub does NOT automatically make its fields (regular or tuple) pub too — they need their own pub to be externally accessible; a common early mistake is making a struct pub and being confused why external code still can't construct it directly or access its fields. Enums, however, have a real asymmetry: an enum's variants are automatically as visible as the enum itself if the enum is pub — worth verifying against up-to-date documentation for the specific case rather than assuming structs and enums behave identically here.
Приватность модуля определяется относительно его собственного РОДИТЕЛЯ, и дочерний модуль всегда видит всё в модулях-предках независимо от того, что эти предки объявили приватным относительно СВОИХ соседей — приватность в Rust принципиально про "видимо этому поддереву иерархии модулей или нет", а не про плоский бинарный публично/приватно, как в одном классе.A module's privacy is relative to its own PARENT, and a child module can always see everything in its ancestors' modules regardless of those ancestors' own privacy declarations relative to THEIR siblings — privacy in Rust is fundamentally about "visible to this subtree of the module hierarchy or not", not a flat public/private binary the way a single class's public/private sections are.
Популярное соглашение разносить реализацию большого модуля по нескольким файлам, представляя его как один логический модуль (mod foo { mod bar; mod baz; pub use bar::Thing; }-подобный паттерн реэкспорта), — вопрос соглашения/стиля проекта поверх этих примитивов, а не отдельная особая фича языка; новички иногда ищут выделенную "фичу слияния пространств имён", хотя на самом деле нужны просто обычные вложенные модули плюс реэкспорты pub use.The popular convention of splitting a large module's implementation across multiple files while presenting it as one logical module (mod foo { mod bar; mod baz; pub use bar::Thing; }-style re-export patterns) is a matter of project convention/style built on top of these primitives, not a separate distinct language feature — newcomers sometimes look for a dedicated "namespace merging" feature when what they actually need is just ordinary nested modules plus pub use re-exports.