Why does rustc allow me to leak a private type?

Viewed 196

I have made a minimal example. In lib.rs:

mod sealed {
    pub enum Choice {
        A,
        B,
    }
}

pub fn print_choice(choice: sealed::Choice) {
    match choice {
        sealed::Choice::A => println!("Choice A"),
        sealed::Choice::B => println!("Choice B"),
    }
}

I think: The enum Choice is public. However, it's in a private mod, and cannot be reached from outside of the crate. Therefore the function print_choice is not callable at all.

What is wrong with my thinking?

3 Answers

What is wrong with my thinking?

You could have something like

pub use sealed::Choice;

at the toplevel. That is a common way to split up the implementation while providing a simple single-module interface to external users.

Or even just an other function returning an instance of Choice. Since it's pub, it's not considered a private type.

If you change pub enum to pub(crate) enum (meaning you state that the enum can not be made visible outside the crate) then the compilation will fail.

An important thing to understand is that Choice is not private. It is inside a private module, and thus unnamable, but it is public.

The only thing the module's privacy affects is that you cannot access the enum via this path. You can do any other thing with it, e.g. accessing it via other path it is reexported into:

mod sealed {
    pub enum Choice {
        A,
        B,
    }
}
pub use sealed::Choice;

// In other module
crate::Choice::A;

Or manipulate it with generics and traits, for example:

mod sealed {
    pub enum Choice {
        A,
        B,
    }
    impl Default for Choice {
        fn default() -> Self { Self::A }
    }
}

pub fn print_choice(choice: sealed::Choice) { ... }

// In other module
crate::print_choice(Default::default());
mod sealed {
    #[derive(Debug)]
    pub enum Choice {
        A,
        B,
    }
}

pub fn print_choice(choice: sealed::Choice) { crate::print(choice) }

// In other module
pub fn print<T: Debug>(v: T) { ... }

This private type seems to leak, but you don't have full control over it.

You cannot build such a private Choice, but another public function can provide you with it.

mod outer {

    mod sealed {
        pub enum Choice {
            A,
            B,
        }
    }

    pub fn print_choice(choice: sealed::Choice) {
        match choice {
            sealed::Choice::A => println!("Choice A"),
            sealed::Choice::B => println!("Choice B"),
        }
    }

    pub fn make_choice(n: u32) -> sealed::Choice {
        if n % 2 == 0 {
            sealed::Choice::A
        } else {
            sealed::Choice::B
        }
    }
}

fn main() {
    // let ch = outer::sealed::Choice::A; // error: module `sealed` is private
    let ch = outer::make_choice(2);
    outer::print_choice(ch);
}
Related