This is my first RFC attempt, corrections are welcome.
Problem
Public structs / enums can currently be declared inside private modules and be used in public interfaces (as function return types or arguments). This is currently legal:
pub mod foo {
mod private_bar {
pub struct PublicBar;
}
pub fn public_to_foo() -> private_bar::PublicBar { private_bar::PublicBar }
}
This is also legal:
pub mod foo {
mod private_bar {
pub struct PublicBar;
}
pub fn public_to_foo(arg: private_bar::PublicBar) { }
}
This, in itself is not a problem. The problem comes when using public_to_foo()
outside of foo
:
fn main() {
let a = foo::public_to_foo(); // this works, but if the return
// is an enum, you can't match it
let b = foo::private_bar::PublicBar; // this doesn't work at all
foo::public_to_foo(b); // so no way to pass the argument to a public function
}
Case A is problematic when a is of an Error
type. Because there is no way to get the underlying type of the error, since the error (plus the error does not get documented using rustdoc
). Playground example
Case B is problematic in any way, making a public function "useless".
Both cases are very real in library design right now. It is up to the library maintainer to "correctly re-export modules".The problem is that it is a pitfall when designing libraries: You want the struct to be public, but the struct / enum isn't actually "public".
What led to this
In the topic [lang-team-minutes] private-in-public rules @nikomatsakis said:
I support the "wrongly so" and this (pre-)RFC is about changing the behaviour to be a warning like #[warn(inaccessible_struct)]
.
I noticed this behaviour here (v.0.5.0): osm_xml::OSM - Rust
Compare this against v.0.5.1 (fixed): osm_xml::OSM - Rust
All because of a missed pub mod errors
. The library designer clearly intended the Error
struct to be public. But the compiler didn't warn that the returned error is effectively useless. You can't implement std::convert::From
on it, you can't match it, the only two things possible are calling unwrap!()
or printing / logging the error and continuing with a half-parsed file. It isn't possible to get the underlying cause and handle a case-by-case "soft fail".
Solution
The rule I would like to introduce, is:
If a struct / enum is used in a public function (such as a argument or return type), the full module path (e.g. library::container::struct
) must be public OR the full module path / the path to the struct must be publicly exported (via pub use
).
This also solves the problem that the returned struct isn't documented (via rustdoc
). For structs, this rule is already enforced (you can't have a nested struct where the nested value is a private struct). For traits, this is also enforced. It would be logical to enforce this in functions, too.
Risks
This should be introduced as a (on-by-default) warning, not a breaking change - after pub(restricted)
is stable. So it would not break anything.
Library maintainers who mistakenly did this in their libraries will correct it.
Library maintainers who did this as a design decision, can either turn the warning off or use pub(restricted)
to limit the scope of their public types.
Thank you for reading.