0

So basically my problem is very similar to the problem presented in this question:

Correct place for private module to be used by multiple executables

My project structure is as follows:

crate
├── Cargo.toml
└── src
    ├── bin
    │   ├── my-bin-1
    │   │   ├── cli.rs
    │   │   ├── main.rs
    │   └── my-bin-2
    │       ├── cli.rs
    │       ├── main.rs
    ├── common.rs
    └── lib.rs

lib.rs contains public code, and common.rs contains private code.

I want to have both of my binaries re-use code from common.rs, but I need this code to remain private. That's why the aformentioned question's solution which is to define a public function in common.rs is not good for my case.

// common.rs
pub fn cant_leak_this_function() {} // uh-oh.. `pub` identifier should NOT be used here

I tried using the pub(crate) identifier, but that made the functions in common.rs private to the binaries.

What I need is some sort of a pub(crate-and-also-the-binaries-in-this-crate-but-not-for-external-crates) specifier (with a shorter name obviously..) which would allow me to use shared private code in my-bin-1/main.rs and in my-bin-2/main.rs, but will also keep that code hidden from external crates.

So far, the only solution that worked for me was creating a mod.rs file inside a common directory within src/bin, (resulting in an awkward src/bin/common/mod.rs path) and then explicitly using the #[path = ..] feature to consume the shared code from the binaries. However, this method caused rust-analyzer to mark the functions from common/mod.rs as unused even though they clearly were used, and even worse, from some reading online I've come to understand that using the #[path = ..] feature is considered bad practice.

Is there a better way to solve this problem? What am I missing here?

Thanks in advance!

wickedlama
  • 26
  • 2
  • 3
  • 1
    Sometimes the "can't leak" requirement is a bit too hard, and just typing `#[doc(hidden)] pub mod private { ... }` is good enough. – rodrigo Aug 30 '22 at 15:01

1 Answers1

2

All items are either public or private outside of the crate. There is no mechanism to give other crates special permission. The best you can do is make it clear that while the function is technically public, it should not be used as such.

Take a trick out of the serde crate, which has some exports that must be visible to macros, but shouldn't be used directly (source):

// Used by generated code and doc tests. Not public API.
#[doc(hidden)]
#[path = "private/mod.rs"]
pub mod __private;

The #[doc(hidden)] means it won't sure up in https://docs.rs or your typical cargo doc invocations. And the __private module should hopefully make it obvious to anyone who does stumble across it that it shouldn't be used like other public items.


If this is still unsavory to you, then the only way to truly make it private is indeed to use #[path = ...] to import the file as modules in your separate binaries as the linked Q&A mentions. Then you don't need a library crate at all. The reason that #[path = ...] is bad practice, is because its easy to get wrong and will deceive those unaware of how it actually works.

The reason you would get an "unused" warning from your common functions is because it is essential duplicating the file. Within the specific binary that it is imported, the function may truly not be used. Even if it were used in the other binary, that wouldn't be considered when emitting that warning.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • Thank you for the detailed response! I ended up following serde's way of solving this issue like you suggested. – wickedlama Aug 30 '22 at 16:32