ProgrammingBackend Developer

Explain how the visibility modifiers work for fields and methods of structures in Rust, as well as the peculiarities of visibility matching of nested structures?

Pass interviews with Hintsage AI assistant

Answer

In the Rust programming language, the level of access (visibility) to the methods and fields of structures is regulated by modifiers: pub, pub(crate), pub(super), as well as the absence of a modifier (by default - private).

Background

Rust was initially designed to ensure reliability and isolation of components. Access control to the internal parts of structures allows encapsulating data and hiding implementation details, keeping only the necessary interfaces public.

Problem

Developers often face situations where a structure is public, but its fields remain private, or the visibility of a field is not sufficient due to visibility constraints of the structure or module itself. It is particularly difficult to understand nested levels: a public nested structure may be inaccessible if the containing module is hidden, and vice versa.

Solution

In Rust, visibility modifiers are applied to structures, their fields and methods, as well as to modules and functions. The following levels exist:

  • pub — makes the element accessible from anywhere.
  • pub(crate) — accessible only within the current crate.
  • pub(super) — accessible only from the parent module.
  • Without a modifier — the element is private within the current module.

Example code:

mod outer { pub struct PublicStruct { pub field: u32, hidden: u32, } pub(crate) struct CrateStruct { pub value: i32, } struct PrivateStruct { pub secret: i32, } pub mod inner { pub(super) struct SuperStruct { pub super_field: u8, } } }

Key features:

  • The visibility of a field or method cannot exceed the visibility of the structure or module itself.
  • For nested structures, the final visibility is determined by the intersection of the modifier and the visibility of all parent modules.
  • Public structures with private fields support the encapsulation pattern (constructors/getter methods).

Trick Questions.

If a structure is declared as pub, but its fields are without a modifier, can they be accessed from another module?

No, only the structure itself becomes public, but its fields remain private within the module. For access to the field, it must also be declared as pub.

What happens if a structure is declared as pub(crate), and a field inside it is pub?

The visibility is limited to the structure itself. Even if the field is pub, it cannot be accessed outside the crate, as the structure is not accessible.

pub(crate) struct Secret { pub data: i32, // pub does not "pass through" pub(crate) }

Can a structure be declared as pub inside a private module and accessed from the outside?

No. The final visibility is determined by the minimum of the structure and the module. If the module is private, the structures and functions inside it are also not visible outside this module.

Common Mistakes and Anti-Patterns

  • Leaving structure fields public when designing complex APIs.
  • Opening the visibility of a structure unnecessarily with ‘pub’.
  • Trying to expand the visibility of a field, ignoring module restrictions.

Real-Life Example

Negative Case

In a project, the entire structure was made public with open fields to speed up development. Later, it became difficult to maintain backward compatibility and control access to fields, as they were modified directly.

Pros:

  • Quick start; no need to implement getters.

Cons:

  • No control over data modification; deterioration of encapsulation.
  • Difficulty in changing the internal structure.

Positive Case

For a public structure, private fields and public constructor/accessor methods are implemented. The structure is exported only at the necessary module level.

Pros:

  • Reliable encapsulation; convenient API.
  • Ability to change the internal implementation without breaking the clients of the code.

Cons:

  • Additional methods have to be written; slightly more code.