Search code examples
ruststructvisibilityrust-rustlings

Rust visibility from child module to outside


I'm learning about Rust, and trying to figure out the visibility rules for modules. I have the following code:

fn x() -> u8 {
    5
}

struct Person {
    name: String,
}

mod example {
    use super::{x, Person};

    pub fn foo(person: &Person) {
        println!("{}", x());
        println!("Person with name: {}", person.name);
    }
}

fn main() {
    let person = Person{name: String::from("PersonName")};
    example::foo(&person);
}

Which throws the following error:

   Compiling playground v0.0.1 (/playground)
error[E0446]: private type `Person` in public interface
  --> src/main.rs:12:5
   |
5  | struct Person {
   | ------------- `Person` declared as private
...
12 |     pub fn foo(person: &Person) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type

For more information about this error, try `rustc --explain E0446`.
error: could not compile `playground` due to previous error
  1. From my understanding, children submodules should have visibility to the symbols from the parent module. I'm not sure why I need to mark Person as public.

  2. If I mark Person as public the code executes, but my next question is, why I don't have to mark the x() function as public also, as it is used at the same place with the Person instance.


Solution

  • Inside the module example it's not known that the module is only visible to super it also might have been reexported somewhere completely different in main.rs:

    pub mod completely_diffrent {
        pub use super::example;
    }
    

    which would be difficult or even impossible to track.

    A solution other than making Person public is to mark the function public only to super:

    mod example {
        use super::{x, Person};
    
        pub(super) fn foo(person: &Person) {
            println!("{}", x());
            println!("Person with name: {}", person.name);
        }
    }
    

    Which avoids all those pitfalls and thus compiles as you can see on the Playground

    For why you'd have to mark Person as public but not x the reason is that Person appears in the signature of foo: fn(&Person) while x does not.