Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make an Rust item public within a crate, but private outside it?

I have a crate that has lots of code, so I've split it into multiple files/modules. However, some modules have internal unsafe stuff (e.g. raw pointers) that I need to make public to the different modules, but I don't want to expose to users of my crate. How can I do that?

The only way I can think of is to actually have my crate just be one big module, but then there's no way to split it into different files, other than this solution which seems a bit hacky.

Normally when I come up against a real world problem that the simple examples in the Rust docs don't adequately explain I just copy a popular real world crate, e.g. git2-rs, but that just seems to effectively make everything public, including the raw pointers.

like image 238
Timmmm Avatar asked Jan 15 '17 20:01

Timmmm


People also ask

How do you make a public function in Rust?

Modules in Rust are private by default. On the contrary, functions in a public module can be accessed by other modules. Modules should be prefixed with pub keyword to make it public.

What is extern crate?

An extern crate declaration specifies a dependency on an external crate. The external crate is then bound into the declaring scope as the identifier provided in the extern crate declaration.

What does Pub mean in Rust?

In addition to public and private, Rust allows users to declare an item as visible within a given scope. The rules for pub restrictions are as follows: pub(in path) makes an item visible within the provided path . path must be a parent module of the item whose visibility is being declared.

How do crates work in Rust?

A crate is the smallest amount of code that the Rust compiler considers at a time. Even if you run rustc rather than cargo and pass a single source code file (as we did all the way back in the “Writing and Running a Rust Program” section of Chapter 1), the compiler considers that file to be a crate.


1 Answers

In order for an item to be exported from a library crate, there must be at least one path leading to it in which every component is public. This means that all you need to make an item public within your crate but not exported from the crate (I'll call this "internal" from now on, to mimic C# terminology) is to put it in a private module under the crate root.

However, that solution is quite restrictive. What if you'd like to have a module with exported functions and internal functions? In order to export some functions, we need to make the module public, and that mean all public items in that module will be exported as well.

Since Rust 1.18, there's a solution adapted to this kind of scenario: pub(restricted). This feature lets you specify "how public" an item should be. The syntax is pretty flexible (you can make an item visible to a particular module tree instead of the whole crate), but if you want to keep it simple, pub(crate) will make an item accessible anywhere within the crate, but not to other crates (equivalent to internal in C#).

For example, suppose we'd like to have a module util in which foo is exported (as mycrate::util::foo), bar is internal and baz is private to the module. The code might look like this:

pub mod util {     pub fn foo() {         unimplemented!()     }      pub(crate) fn bar() {         unimplemented!()     }      fn baz() {         unimplemented!()     } } 

If you're stuck on pre-1.18 Rust, there's a workaround, but it's a bit clunky. It involves defining all your items in private modules, and reexporting only those that you want to make public (with pub use) in public modules that only contain reexports. Here's what the example above would look like:

pub mod util {     pub use util_impl::foo; }  mod util_impl {     pub fn foo() {         unimplemented!()     }      pub fn bar() {         unimplemented!()     }      fn baz() {         unimplemented!()     } } 

Not only is this not easy to read and understand, it doesn't cover all situations where pub can be used. For example, how would you make some fields of an exported struct accessible in other modules in the same crate without also exporting them? The only option would be to expose a wrapper with a single private field whose type is the struct that has public fields; that works fine if you want to hide all fields from other crates, but not if you want to expose some fields and make some other fields internal in the same struct.

like image 120
Francis Gagné Avatar answered Sep 22 '22 04:09

Francis Gagné