In the below example, the module outer
has a private type Private
and a private inner module inner
. inner
is able to access Private
(because child modules can access their parent's private items, even if they are not parked as public).
inner
defines a function not_really_public_interface()
. While it is marked as public, it is really only available to outer
because inner
itself is not public.
outer.rs
struct Private;
mod inner {
use super::Private;
pub fn not_really_public_interface() -> Private {
Private
}
}
This compiles without any problems.
outer
should be able to use inner::not_really_public_interface()
to obtain Private
, as long as it makes sure not to export it. So let's do that:
pub fn main() {
let _ = self::inner::not_really_public_interface();
}
Right?
stderr
error[E0446]: private type `Private` in public interface
--> src/outer.rs:4:3
|
4 | / pub fn not_really_public_interface() -> Private {
5 | | Private
6 | | }
| |___^ can't leak private type
Wat. This is counter-intuitive to me for several reasons:
outer
attempt to use this function.inner
could possibly "leak" Private
is to the module that defined it.So my questions are:
Private
as if it were defined in inner
.Private
public within outer
and inner
, but I'd prefer not to do that.The function not_really_public_interface
is public so it could be used by any other module. But the Private
struct can only be accessed by your root and inner
modules.
The leak would occur if another module imported not_really_public_interface
. Rust is complaining that this could happen because it reports errors locally, rather than taking a "whole world" view across all usages in all modules and crates. Ultimately, this approach is more predictable for humans to reason about and faster for the machine.
Rust lets you control the visibility more precisely though. If you tell it that the function is only available to the module one level up (the super
module) then it knows there is no possibility of a leak:
mod inner {
use super::Private;
pub(super) fn not_really_public_interface() -> Private { Private }
}
You could also use crate
instead of super
, to mean any module in the same crate. Or, if the super module had a name, e.g. my_mod
, you could use pub(in ::my_mod)
to target it specifically.