Crate-level visibility

207 views Asked by At

I have two types, each with a bunch of supporting functions; the entire content of these two types should be private (in my case, they are mutable pointers to objects in C). These two types are conceptually different, so I put them in different modules…

pub mod client {
    pub struct Client {
        // private internals
    }
    // Lots of code.
}
pub mod collection {
    pub struct Collection {
        // private internals
    }
    // Lots of code.
}

(In my real-world case, mod client and mod collection are separate files, i.e., client.rs and collection.rs.)

client.rs needs to create a Collection; however, it can't, because the internals are private. Any attempt to write a function suffers the same problem: the function would need to be in collection.rs (to access the private members of Collection, but would need to be pub so that client.rs can access it… but now it's also pub to the entire world.

What I'd really like is some sort of crate-level visibility, or a way to "friend" this struct out to another module. (Otherwise, the publically visible stuff in the crate doesn't represent the API, which seems silly.)

2

There are 2 answers

1
Matthieu M. On

If you compare Rust and C++ you will notice that Rust does not have:

  • protected
  • friend

This is a deliberate design, and there is no alternative. In Rust something is either private or public, and therefore you do not have to jump all around the codebase to know whether an invariant holds or not.

Therefore, the solution is to:

  • make a pub fn new function to create a Collection
  • reinforce this function so it is perfectly safe to use by the whole world

This is actually good design; if there is one struct that should be responsible for upholding Collection's invariants, it certainly is Collection itself. Delegating this responsibility to another crate seems hazardous.

0
Francis Gagné On

Move Collection to a new private module (e.g. collection_impl). Add a public function in that module to create a Collection. Re-export Collection from collection (which is public) with pub use.

You can use collection_impl from within your crate, but since it is private, other crates cannot use it. However, by re-exporting Collection in module collection, other crates can use Collection through this path.

pub mod client {
    use super::collection;
    use super::collection_impl;

    pub struct Client {
        p: i32
    }

    /* this could also be a method on Client */
    pub fn make_collection(client: &Client) -> collection::Collection {
        collection_impl::new_collection(42)
    }
}

mod collection_impl {
    pub struct Collection {
        p: i32
    }

    pub fn new_collection(p: i32) -> Collection {
        Collection { p: p }
    }
}

pub mod collection {
    use super::collection_impl;
    pub use collection_impl::Collection;
}