How do I make an Rust item public within a crate,

2019-02-03 02:45发布

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.

1条回答
趁早两清
2楼-- · 2019-02-03 03:36

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.

查看更多
登录 后发表回答