5

To make methods from a submodule public, you have to explicitly re-export them or else make the submodule itself public:

mod foo {
    mod bar {
        pub fn baz() {}
    }
    pub use self::bar::baz;
}

This seems to hint that pub is used to indicate a thing should be public just to a module (since you can choose not to do this)

But if you use a private type defined in the outer context, you get an error if you try and make a public function involving it public in the inner context even when it's not re-exported.

mod foo {
    struct Foo;

    mod bar {
        use super::Foo;

        pub fn baz(foo: Foo) {}
    }
}

results in

error[E0446]: private type `Foo` in public interface
 --> src/lib.rs:7:9
  |
2 |     struct Foo;
  |     - `Foo` declared as private
...
7 |         pub fn baz(foo: Foo) {}
  |         ^^^^^^^^^^^^^^^^^^^^ can't leak private type

What's the idiomatic way to use the pub keyword? Should it be reserved for things that are actually public or can it be used for internal modules?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
dspyz
  • 5,280
  • 2
  • 25
  • 63

1 Answers1

2

The reason your example fails to compile, is ultimately because the RFC (136) said so. (See also issue 22261)

What restrictions apply to public items?

The rules for various kinds of public items are as follows:

  • If it is a static declaration, items referred to in its type must be public.
  • If it is an fn declaration, items referred to in its trait bounds, argument types, and return type must be public.
  • If it is a struct or enum declaration, items referred to in its trait bounds and in the types of its pub fields must be public.
  • If it is a type declaration, items referred to in its definition must be public.
  • If it is a trait declaration, items referred to in its super-traits, in the trait bounds of its type parameters, and in the signatures of its methods (see fn case above) must be public.

In short, baz is not allowed to be pub because it has an argument which is a private type. Thus, if baz is pub it would allow the parent mod foo to re-export baz by doing pub use bar::baz;. This is of course not allowed, and this is the reason why the example as a whole is illegal.

Some have previously mentioned that pub fn baz should be allowed, and instead to give a compile error in the event that the parent module re-exports it. However, that would require more complex static analysis to detect, and ultimately has not been done because the RFC defined that it is illegal.


pub specifies that the item is accessible by the parent module. If all module ancestors are pub, then that item is exported by the crate as a whole.

The keyword pub makes any module, function, or data structure accessible from inside of external modules. The pub keyword may also be used in a use declaration to re-export an identifier from a namespace.

Rust Documentation

With the notion of an item being either public or private, Rust allows item accesses in two cases:

  1. If an item is public, then it can be accessed externally from some module m if you can access all the item's ancestor modules from m. You can also potentially be able to name the item through re-exports. See below.
  2. If an item is private, it may be accessed by the current module and its descendants.

The Rust Reference - Visibility and Privacy

vallentin
  • 23,478
  • 6
  • 59
  • 81
  • 2
    I understand the mechanics of _how_ pub works. I'm confused about when I should use it. Why does the compiler complain about private-type-in-public-interface in my example? – dspyz Dec 14 '20 at 21:10
  • Note that I tagged this question with `conventions` – dspyz Dec 14 '20 at 21:11
  • 1
    Alright, give me a minute, and I'll redo the answer :) – vallentin Dec 14 '20 at 21:13